This commit was manufactured by cvs2svn to create branch 'vserver'.
authorPlanet-Lab Support <support@planet-lab.org>
Mon, 8 Aug 2005 21:11:56 +0000 (21:11 +0000)
committerPlanet-Lab Support <support@planet-lab.org>
Mon, 8 Aug 2005 21:11:56 +0000 (21:11 +0000)
301 files changed:
Documentation/aoe/todo.txt [new file with mode: 0644]
Documentation/aoe/udev.txt [new file with mode: 0644]
Documentation/cpu-freq/cpufreq-stats.txt [new file with mode: 0644]
Documentation/cpusets.txt [new file with mode: 0644]
Documentation/dontdiff [new file with mode: 0644]
Documentation/dvb/ci.txt [new file with mode: 0644]
Documentation/i2c/busses/i2c-amd8111 [new file with mode: 0644]
Documentation/i2c/busses/i2c-i801 [new file with mode: 0644]
Documentation/i2c/busses/i2c-nforce2 [new file with mode: 0644]
Documentation/i2c/busses/i2c-parport [new file with mode: 0644]
Documentation/i2c/busses/i2c-piix4 [new file with mode: 0644]
Documentation/i2c/busses/i2c-sis69x [new file with mode: 0644]
Documentation/i2c/busses/i2c-viapro [new file with mode: 0644]
Documentation/i2c/busses/scx200_acb [new file with mode: 0644]
Documentation/usb/usbmon.txt [new file with mode: 0644]
arch/arm/configs/pxa255-idp_defconfig [new file with mode: 0644]
arch/arm/lib/bitops.h [new file with mode: 0644]
arch/arm/mach-omap/board-netstar.c [new file with mode: 0644]
arch/arm/mach-omap/board-voiceblue.c [new file with mode: 0644]
arch/arm/mach-omap/leds-osk.c [new file with mode: 0644]
arch/arm/mach-pxa/poodle.c [new file with mode: 0644]
arch/arm/mach-s3c2410/mach-n30.c [new file with mode: 0644]
arch/arm/mach-s3c2410/mach-nexcoder.c [new file with mode: 0644]
arch/arm/mach-s3c2410/mach-otom.c [new file with mode: 0644]
arch/arm/mach-s3c2410/mach-smdk2440.c [new file with mode: 0644]
arch/arm/mm/copypage-v4mc.c [new file with mode: 0644]
arch/arm/mm/copypage-xscale.c [new file with mode: 0644]
arch/i386/kernel/cpu/cpufreq/sc520_freq.c [new file with mode: 0644]
arch/i386/kernel/reboot_fixups.c [new file with mode: 0644]
arch/ia64/sn/kernel/tiocx.c [new file with mode: 0644]
arch/ia64/sn/kernel/xpc.h [new file with mode: 0644]
arch/ia64/sn/kernel/xpc_channel.c [new file with mode: 0644]
arch/ia64/sn/kernel/xpc_main.c [new file with mode: 0644]
arch/ia64/sn/kernel/xpc_partition.c [new file with mode: 0644]
arch/ia64/sn/kernel/xpnet.c [new file with mode: 0644]
arch/ia64/sn/pci/tioca_provider.c [new file with mode: 0644]
arch/m68knommu/platform/68328/head-pilot.S [new file with mode: 0644]
arch/m68knommu/platform/68328/head-ram.S [new file with mode: 0644]
arch/m68knommu/platform/68328/head-rom.S [new file with mode: 0644]
arch/ppc/boot/simple/misc-chestnut.c [new file with mode: 0644]
arch/ppc/boot/simple/misc-ev64260.c [new file with mode: 0644]
arch/ppc/boot/simple/misc-mv64x60.c [new file with mode: 0644]
arch/ppc/boot/simple/misc-radstone_ppc7d.c [new file with mode: 0644]
arch/ppc/boot/simple/openbios.c [new file with mode: 0644]
arch/ppc/kernel/fpu.S [new file with mode: 0644]
arch/ppc/kernel/head_fsl_booke.S [new file with mode: 0644]
arch/ppc/kernel/swsusp.S [new file with mode: 0644]
arch/ppc/platforms/83xx/mpc834x_sys.c [new file with mode: 0644]
arch/ppc/platforms/83xx/mpc834x_sys.h [new file with mode: 0644]
arch/ppc/platforms/chrp_pegasos_eth.c [new file with mode: 0644]
arch/ppc/platforms/hdpu.c [new file with mode: 0644]
arch/ppc/platforms/hdpu.h [new file with mode: 0644]
arch/ppc/platforms/radstone_ppc7d.c [new file with mode: 0644]
arch/ppc/platforms/radstone_ppc7d.h [new file with mode: 0644]
arch/ppc/syslib/ipic.c [new file with mode: 0644]
arch/ppc/syslib/ipic.h [new file with mode: 0644]
arch/ppc/syslib/m82xx_pci.c [new file with mode: 0644]
arch/ppc/syslib/mpc52xx_devices.c [new file with mode: 0644]
arch/ppc/syslib/mpc52xx_pci.c [new file with mode: 0644]
arch/ppc/syslib/mpc52xx_pci.h [new file with mode: 0644]
arch/ppc/syslib/mpc52xx_sys.c [new file with mode: 0644]
arch/ppc/syslib/mpc83xx_devices.c [new file with mode: 0644]
arch/ppc/syslib/mpc83xx_sys.c [new file with mode: 0644]
arch/ppc/syslib/mpc85xx_devices.c [new file with mode: 0644]
arch/ppc/syslib/mpc85xx_sys.c [new file with mode: 0644]
arch/ppc/syslib/ppc83xx_setup.c [new file with mode: 0644]
arch/ppc/syslib/ppc83xx_setup.h [new file with mode: 0644]
arch/ppc64/kernel/pSeries_reconfig.c [new file with mode: 0644]
arch/ppc64/kernel/pmc.c [new file with mode: 0644]
arch/ppc64/kernel/vdso.c [new file with mode: 0644]
arch/ppc64/kernel/vdso32/Makefile [new file with mode: 0644]
arch/ppc64/kernel/vdso32/cacheflush.S [new file with mode: 0644]
arch/ppc64/kernel/vdso32/datapage.S [new file with mode: 0644]
arch/ppc64/kernel/vdso32/gettimeofday.S [new file with mode: 0644]
arch/ppc64/kernel/vdso32/note.S [new file with mode: 0644]
arch/ppc64/kernel/vdso32/sigtramp.S [new file with mode: 0644]
arch/ppc64/kernel/vdso32/vdso32.lds.S [new file with mode: 0644]
arch/ppc64/kernel/vdso32/vdso32_wrapper.S [new file with mode: 0644]
arch/ppc64/kernel/vdso64/Makefile [new file with mode: 0644]
arch/ppc64/kernel/vdso64/cacheflush.S [new file with mode: 0644]
arch/ppc64/kernel/vdso64/datapage.S [new file with mode: 0644]
arch/ppc64/kernel/vdso64/gettimeofday.S [new file with mode: 0644]
arch/ppc64/kernel/vdso64/note.S [new file with mode: 0644]
arch/ppc64/kernel/vdso64/sigtramp.S [new file with mode: 0644]
arch/ppc64/kernel/vdso64/vdso64.lds.S [new file with mode: 0644]
arch/ppc64/kernel/vdso64/vdso64_wrapper.S [new file with mode: 0644]
arch/sh/boards/hp6xx/hp620/setup.c [new file with mode: 0644]
arch/sh/configs/se7750_defconfig [new file with mode: 0644]
arch/sh64/lib/iomap.c [new file with mode: 0644]
arch/sh64/mach-cayman/iomap.c [new file with mode: 0644]
arch/sparc64/lib/bzero.S [new file with mode: 0644]
arch/um/drivers/random.c [new file with mode: 0644]
arch/um/include/common-offsets.h [new file with mode: 0644]
arch/um/os-Linux/util/Makefile [new file with mode: 0644]
arch/um/os-Linux/util/mk_user_constants.c [new file with mode: 0644]
arch/um/scripts/Makefile.rules [new file with mode: 0644]
arch/um/sys-i386/kernel-offsets.c [new file with mode: 0644]
arch/um/sys-i386/sys_call_table.S [new file with mode: 0644]
arch/um/sys-i386/user-offsets.c [new file with mode: 0644]
arch/um/sys-i386/util/mk_thread.c [new file with mode: 0644]
arch/um/sys-x86_64/kernel-offsets.c [new file with mode: 0644]
arch/um/sys-x86_64/ksyms.c [new file with mode: 0644]
arch/um/sys-x86_64/syscall_table.c [new file with mode: 0644]
arch/um/sys-x86_64/user-offsets.c [new file with mode: 0644]
arch/um/sys-x86_64/util/mk_thread.c [new file with mode: 0644]
arch/um/util/mk_constants.c [new file with mode: 0644]
arch/um/util/mk_task.c [new file with mode: 0644]
arch/x86_64/kernel/pmtimer.c [new file with mode: 0644]
crypto/tgr192.c [new file with mode: 0644]
drivers/acpi/acpi_memhotplug.c [new file with mode: 0644]
drivers/char/agp/sgi-agp.c [new file with mode: 0644]
drivers/char/mbcs.c [new file with mode: 0644]
drivers/char/snsc_event.c [new file with mode: 0644]
drivers/char/tb0219.c [new file with mode: 0644]
drivers/char/tpm/Kconfig [new file with mode: 0644]
drivers/char/tpm/Makefile [new file with mode: 0644]
drivers/char/tpm/tpm.c [new file with mode: 0644]
drivers/char/tpm/tpm.h [new file with mode: 0644]
drivers/char/tpm/tpm_atmel.c [new file with mode: 0644]
drivers/char/tpm/tpm_nsc.c [new file with mode: 0644]
drivers/char/vr41xx_rtc.c [new file with mode: 0644]
drivers/cpufreq/cpufreq_conservative.c [new file with mode: 0644]
drivers/i2c/busses/i2c-mv64xxx.c [new file with mode: 0644]
drivers/i2c/chips/ds1337.c [new file with mode: 0644]
drivers/i2c/chips/fscpos.c [new file with mode: 0644]
drivers/i2c/chips/gl520sm.c [new file with mode: 0644]
drivers/i2c/chips/lm92.c [new file with mode: 0644]
drivers/i2c/chips/m41t00.c [new file with mode: 0644]
drivers/i2c/chips/sis5595.c [new file with mode: 0644]
drivers/infiniband/hw/mthca/mthca_uar.c [new file with mode: 0644]
drivers/input/keyboard/corgikbd.c [new file with mode: 0644]
drivers/input/keyboard/hil_kbd.c [new file with mode: 0644]
drivers/input/keyboard/hilkbd.c [new file with mode: 0644]
drivers/input/keyboard/locomokbd.c [new file with mode: 0644]
drivers/input/misc/hp_sdc_rtc.c [new file with mode: 0644]
drivers/input/mouse/hil_ptr.c [new file with mode: 0644]
drivers/input/serio/hil_mlc.c [new file with mode: 0644]
drivers/input/serio/hp_sdc.c [new file with mode: 0644]
drivers/input/serio/hp_sdc_mlc.c [new file with mode: 0644]
drivers/input/touchscreen/corgi_ts.c [new file with mode: 0644]
drivers/input/touchscreen/elo.c [new file with mode: 0644]
drivers/input/touchscreen/hp680_ts_input.c [new file with mode: 0644]
drivers/input/touchscreen/mk712.c [new file with mode: 0644]
drivers/input/touchscreen/mtouch.c [new file with mode: 0644]
drivers/isdn/hisax/hfc4s8s_l1.c [new file with mode: 0644]
drivers/isdn/hisax/hfc4s8s_l1.h [new file with mode: 0644]
drivers/isdn/hisax/hfc_usb.h [new file with mode: 0644]
drivers/macintosh/smu.c [new file with mode: 0644]
drivers/md/dm-emc.c [new file with mode: 0644]
drivers/md/dm-hw-handler.c [new file with mode: 0644]
drivers/md/dm-hw-handler.h [new file with mode: 0644]
drivers/md/dm-mpath.c [new file with mode: 0644]
drivers/md/dm-mpath.h [new file with mode: 0644]
drivers/md/dm-path-selector.c [new file with mode: 0644]
drivers/md/dm-path-selector.h [new file with mode: 0644]
drivers/md/dm-round-robin.c [new file with mode: 0644]
drivers/media/dvb/b2c2/flexcop-common.h [new file with mode: 0644]
drivers/media/dvb/b2c2/flexcop-fe-tuner.c [new file with mode: 0644]
drivers/media/dvb/b2c2/flexcop-i2c.c [new file with mode: 0644]
drivers/media/dvb/b2c2/flexcop-pci.c [new file with mode: 0644]
drivers/media/dvb/b2c2/flexcop-usb.c [new file with mode: 0644]
drivers/media/dvb/b2c2/flexcop.c [new file with mode: 0644]
drivers/media/dvb/bt8xx/dst_ca.c [new file with mode: 0644]
drivers/media/dvb/bt8xx/dst_common.h [new file with mode: 0644]
drivers/media/dvb/dibusb/dvb-fe-dtt200u.c [new file with mode: 0644]
drivers/media/dvb/frontends/dvb-pll.c [new file with mode: 0644]
drivers/media/dvb/frontends/dvb-pll.h [new file with mode: 0644]
drivers/media/dvb/frontends/or51132.c [new file with mode: 0644]
drivers/media/dvb/frontends/or51132.h [new file with mode: 0644]
drivers/media/dvb/frontends/or51211.c [new file with mode: 0644]
drivers/media/dvb/frontends/or51211.h [new file with mode: 0644]
drivers/media/video/cx88/cx88-input.c [new file with mode: 0644]
drivers/media/video/tda8290.c [new file with mode: 0644]
drivers/media/video/tuner-core.c [new file with mode: 0644]
drivers/media/video/tuner-simple.c [new file with mode: 0644]
drivers/net/bnx2.c [new file with mode: 0644]
drivers/net/bnx2.h [new file with mode: 0644]
drivers/net/bnx2_fw.h [new file with mode: 0644]
drivers/parisc/pdc_stable.c [new file with mode: 0644]
drivers/parport/parport_gsc.h [new file with mode: 0644]
drivers/s390/net/claw.c [new file with mode: 0644]
drivers/s390/net/claw.h [new file with mode: 0644]
drivers/s390/net/ctcmain.h [new file with mode: 0644]
drivers/s390/net/qeth_eddp.c [new file with mode: 0644]
drivers/s390/net/qeth_eddp.h [new file with mode: 0644]
drivers/s390/net/qeth_tso.h [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_attr.c [new file with mode: 0644]
drivers/scsi/lpfc/lpfc_scsi.c [new file with mode: 0644]
drivers/scsi/qla2xxx/qla_attr.c [new file with mode: 0644]
drivers/serial/ioc4_serial.c [new file with mode: 0644]
drivers/serial/jsm/jsm.h [new file with mode: 0644]
drivers/serial/jsm/jsm_driver.c [new file with mode: 0644]
drivers/serial/jsm/jsm_neo.c [new file with mode: 0644]
drivers/serial/jsm/jsm_tty.c [new file with mode: 0644]
drivers/serial/vr41xx_siu.c [new file with mode: 0644]
drivers/sn/Makefile [new file with mode: 0644]
drivers/sn/ioc4.c [new file with mode: 0644]
drivers/usb/host/ohci-ppc-soc.c [new file with mode: 0644]
drivers/usb/host/sl811_cs.c [new file with mode: 0644]
drivers/usb/host/uhci-q.c [new file with mode: 0644]
drivers/usb/media/pwc/philips.txt [new file with mode: 0644]
drivers/usb/media/pwc/pwc-kiara.h [new file with mode: 0644]
drivers/usb/media/pwc/pwc-misc.c [new file with mode: 0644]
drivers/usb/media/pwc/pwc-nala.h [new file with mode: 0644]
drivers/usb/media/pwc/pwc-timon.h [new file with mode: 0644]
drivers/usb/media/pwc/pwc-uncompress.h [new file with mode: 0644]
drivers/usb/misc/sisusbvga/sisusb.c [new file with mode: 0644]
drivers/usb/misc/sisusbvga/sisusb.h [new file with mode: 0644]
drivers/usb/mon/mon_main.c [new file with mode: 0644]
drivers/usb/mon/mon_stat.c [new file with mode: 0644]
drivers/usb/mon/mon_text.c [new file with mode: 0644]
drivers/usb/mon/usb_mon.h [new file with mode: 0644]
drivers/usb/net/zd1201.c [new file with mode: 0644]
drivers/usb/net/zd1201.h [new file with mode: 0644]
drivers/usb/serial/airprime.c [new file with mode: 0644]
drivers/usb/serial/cp2101.c [new file with mode: 0644]
drivers/usb/serial/hp4x.c [new file with mode: 0644]
drivers/usb/serial/option.c [new file with mode: 0644]
drivers/video/geode/Kconfig [new file with mode: 0644]
drivers/video/geode/Makefile [new file with mode: 0644]
drivers/video/geode/gx1fb_core.c [new file with mode: 0644]
drivers/video/imxfb.c [new file with mode: 0644]
drivers/video/nvidia/Makefile [new file with mode: 0644]
drivers/video/nvidia/nv_accel.c [new file with mode: 0644]
drivers/video/nvidia/nv_hw.c [new file with mode: 0644]
drivers/video/nvidia/nv_i2c.c [new file with mode: 0644]
drivers/video/nvidia/nv_local.h [new file with mode: 0644]
drivers/video/nvidia/nv_of.c [new file with mode: 0644]
drivers/video/nvidia/nv_proto.h [new file with mode: 0644]
drivers/video/nvidia/nv_setup.c [new file with mode: 0644]
drivers/video/nvidia/nv_type.h [new file with mode: 0644]
drivers/video/nvidia/nvidia.c [new file with mode: 0644]
drivers/video/s1d13xxxfb.c [new file with mode: 0644]
drivers/video/savage/savagefb_driver.c [new file with mode: 0644]
fs/cifs/cifsencrypt.h [new file with mode: 0644]
fs/cifs/ioctl.c [new file with mode: 0644]
fs/fat/fatent.c [new file with mode: 0644]
fs/isofs/isofs.h [new file with mode: 0644]
fs/xfs/linux-2.6/xfs_export.h [new file with mode: 0644]
include/asm-arm/arch-omap/aic23.h [new file with mode: 0644]
include/asm-arm/arch-omap/board-netstar.h [new file with mode: 0644]
include/asm-arm/arch-pxa/poodle.h [new file with mode: 0644]
include/asm-arm/arch-s3c2410/regs-adc.h [new file with mode: 0644]
include/asm-generic/signal.h [new file with mode: 0644]
include/asm-ia64/sn/pcibus_provider_defs.h [new file with mode: 0644]
include/asm-ia64/sn/pcidev.h [new file with mode: 0644]
include/asm-ia64/sn/tioca_provider.h [new file with mode: 0644]
include/asm-ia64/sn/xp.h [new file with mode: 0644]
include/asm-ppc/ipic.h [new file with mode: 0644]
include/asm-ppc/mpc83xx.h [new file with mode: 0644]
include/asm-ppc64/agp.h [new file with mode: 0644]
include/asm-ppc64/imalloc.h [new file with mode: 0644]
include/asm-ppc64/pSeries_reconfig.h [new file with mode: 0644]
include/asm-ppc64/pmc.h [new file with mode: 0644]
include/asm-ppc64/seccomp.h [new file with mode: 0644]
include/asm-ppc64/smu.h [new file with mode: 0644]
include/asm-ppc64/vdso.h [new file with mode: 0644]
include/asm-sh/cpu-sh3/timer.h [new file with mode: 0644]
include/asm-sh/sh03/ide.h [new file with mode: 0644]
include/asm-um/elf-ppc.h [new file with mode: 0644]
include/linux/cpuset.h [new file with mode: 0644]
include/linux/ioc4_common.h [new file with mode: 0644]
include/linux/seccomp.h [new file with mode: 0644]
include/linux/usb_cdc.h [new file with mode: 0644]
include/net/act_generic.h [new file with mode: 0644]
include/net/ip_mp_alg.h [new file with mode: 0644]
include/net/tc_act/tc_defact.h [new file with mode: 0644]
include/sound/ak4114.h [new file with mode: 0644]
include/video/s1d13xxxfb.h [new file with mode: 0644]
kernel/cpuset.c [new file with mode: 0644]
kernel/posix-cpu-timers.c [new file with mode: 0644]
lib/sha1.c [new file with mode: 0644]
lib/sort.c [new file with mode: 0644]
net/ipv4/multipath_drr.c [new file with mode: 0644]
net/ipv4/multipath_random.c [new file with mode: 0644]
net/ipv4/multipath_rr.c [new file with mode: 0644]
net/ipv4/multipath_wrandom.c [new file with mode: 0644]
net/sched/cls_basic.c [new file with mode: 0644]
net/sched/em_cmp.c [new file with mode: 0644]
net/sched/em_meta.c [new file with mode: 0644]
net/sched/em_nbyte.c [new file with mode: 0644]
net/sched/em_u32.c [new file with mode: 0644]
net/sched/ematch.c [new file with mode: 0644]
net/sched/simple.c [new file with mode: 0644]
sound/core/control_compat.c [new file with mode: 0644]
sound/core/hwdep_compat.c [new file with mode: 0644]
sound/core/pcm_compat.c [new file with mode: 0644]
sound/i2c/other/ak4114.c [new file with mode: 0644]
sound/pci/emu10k1/p16v.c [new file with mode: 0644]
sound/pci/hda/Makefile [new file with mode: 0644]
sound/pci/hda/hda_codec.c [new file with mode: 0644]
sound/pci/hda/hda_codec.h [new file with mode: 0644]
sound/pci/hda/hda_generic.c [new file with mode: 0644]
sound/pci/hda/hda_intel.c [new file with mode: 0644]
sound/pci/hda/hda_local.h [new file with mode: 0644]
sound/pci/hda/hda_patch.h [new file with mode: 0644]
sound/pci/hda/hda_proc.c [new file with mode: 0644]
sound/pci/hda/patch_analog.c [new file with mode: 0644]
sound/pci/hda/patch_realtek.c [new file with mode: 0644]
sound/pci/ice1712/phase.c [new file with mode: 0644]
sound/ppc/toonie.c [new file with mode: 0644]

diff --git a/Documentation/aoe/todo.txt b/Documentation/aoe/todo.txt
new file mode 100644 (file)
index 0000000..7fee1e1
--- /dev/null
@@ -0,0 +1,14 @@
+There is a potential for deadlock when allocating a struct sk_buff for
+data that needs to be written out to aoe storage.  If the data is
+being written from a dirty page in order to free that page, and if
+there are no other pages available, then deadlock may occur when a
+free page is needed for the sk_buff allocation.  This situation has
+not been observed, but it would be nice to eliminate any potential for
+deadlock under memory pressure.
+
+Because ATA over Ethernet is not fragmented by the kernel's IP code,
+the destructore member of the struct sk_buff is available to the aoe
+driver.  By using a mempool for allocating all but the first few
+sk_buffs, and by registering a destructor, we should be able to
+efficiently allocate sk_buffs without introducing any potential for
+deadlock.
diff --git a/Documentation/aoe/udev.txt b/Documentation/aoe/udev.txt
new file mode 100644 (file)
index 0000000..ab39d8b
--- /dev/null
@@ -0,0 +1,23 @@
+# These rules tell udev what device nodes to create for aoe support.
+# They may be installed along the following lines (adjusted to what
+# you see on your system).
+# 
+#   ecashin@makki ~$ su
+#   Password:
+#   bash# find /etc -type f -name udev.conf
+#   /etc/udev/udev.conf
+#   bash# grep udev_rules= /etc/udev/udev.conf
+#   udev_rules="/etc/udev/rules.d/"
+#   bash# ls /etc/udev/rules.d/
+#   10-wacom.rules  50-udev.rules
+#   bash# cp /path/to/linux-2.6.xx/Documentation/aoe/udev.txt \
+#           /etc/udev/rules.d/60-aoe.rules
+#  
+
+# aoe char devices
+SUBSYSTEM="aoe", KERNEL="discover",    NAME="etherd/%k", GROUP="disk", MODE="0220"
+SUBSYSTEM="aoe", KERNEL="err",         NAME="etherd/%k", GROUP="disk", MODE="0440"
+SUBSYSTEM="aoe", KERNEL="interfaces",  NAME="etherd/%k", GROUP="disk", MODE="0220"
+
+# aoe block devices     
+KERNEL="etherd*",       NAME="%k", GROUP="disk"
diff --git a/Documentation/cpu-freq/cpufreq-stats.txt b/Documentation/cpu-freq/cpufreq-stats.txt
new file mode 100644 (file)
index 0000000..e2d1e76
--- /dev/null
@@ -0,0 +1,128 @@
+
+     CPU frequency and voltage scaling statictics in the Linux(TM) kernel
+
+
+             L i n u x    c p u f r e q - s t a t s   d r i v e r
+
+                       - information for users -
+
+
+             Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
+
+Contents
+1. Introduction
+2. Statistics Provided (with example)
+3. Configuring cpufreq-stats
+
+
+1. Introduction
+
+cpufreq-stats is a driver that provices CPU frequency statistics for each CPU.
+This statistics is provided in /sysfs as a bunch of read_only interfaces. This
+interface (when configured) will appear in a seperate directory under cpufreq
+in /sysfs (<sysfs root>/devices/system/cpu/cpuX/cpufreq/stats/) for each CPU.
+Various statistics will form read_only files under this directory.
+
+This driver is designed to be independent of any particular cpufreq_driver
+that may be running on your CPU. So, it will work with any cpufreq_driver.
+
+
+2. Statistics Provided (with example)
+
+cpufreq stats provides following statistics (explained in detail below).
+-  time_in_state
+-  total_trans
+-  trans_table
+
+All the statistics will be from the time the stats driver has been inserted 
+to the time when a read of a particular statistic is done. Obviously, stats 
+driver will not have any information about the the frequcny transitions before
+the stats driver insertion.
+
+--------------------------------------------------------------------------------
+<mysystem>:/sys/devices/system/cpu/cpu0/cpufreq/stats # ls -l
+total 0
+drwxr-xr-x  2 root root    0 May 14 16:06 .
+drwxr-xr-x  3 root root    0 May 14 15:58 ..
+-r--r--r--  1 root root 4096 May 14 16:06 time_in_state
+-r--r--r--  1 root root 4096 May 14 16:06 total_trans
+-r--r--r--  1 root root 4096 May 14 16:06 trans_table
+--------------------------------------------------------------------------------
+
+-  time_in_state
+This gives the amount of time spent in each of the frequencies supported by
+this CPU. The cat output will have "<frequency> <time>" pair in each line, which
+will mean this CPU spent <time> usertime units of time at <frequency>. Output
+will have one line for each of the supported freuencies. usertime units here 
+is 10mS (similar to other time exported in /proc).
+
+--------------------------------------------------------------------------------
+<mysystem>:/sys/devices/system/cpu/cpu0/cpufreq/stats # cat time_in_state 
+3600000 2089
+3400000 136
+3200000 34
+3000000 67
+2800000 172488
+--------------------------------------------------------------------------------
+
+
+-  total_trans
+This gives the total number of frequency transitions on this CPU. The cat 
+output will have a single count which is the total number of frequency
+transitions.
+
+--------------------------------------------------------------------------------
+<mysystem>:/sys/devices/system/cpu/cpu0/cpufreq/stats # cat total_trans
+20
+--------------------------------------------------------------------------------
+
+-  trans_table
+This will give a fine grained information about all the CPU frequency
+transitions. The cat output here is a two dimensional matrix, where an entry
+<i,j> (row i, column j) represents the count of number of transitions from 
+Freq_i to Freq_j. Freq_i is in descending order with increasing rows and 
+Freq_j is in descending order with increasing columns. The output here also 
+contains the actual freq values for each row and column for better readability.
+
+--------------------------------------------------------------------------------
+<mysystem>:/sys/devices/system/cpu/cpu0/cpufreq/stats # cat trans_table
+   From  :    To
+         :   3600000   3400000   3200000   3000000   2800000 
+  3600000:         0         5         0         0         0 
+  3400000:         4         0         2         0         0 
+  3200000:         0         1         0         2         0 
+  3000000:         0         0         1         0         3 
+  2800000:         0         0         0         2         0 
+--------------------------------------------------------------------------------
+
+
+3. Configuring cpufreq-stats
+
+To configure cpufreq-stats in your kernel
+Config Main Menu
+       Power management options (ACPI, APM)  --->
+               CPU Frequency scaling  --->
+                       [*] CPU Frequency scaling
+                       <*>   CPU frequency translation statistics 
+                       [*]     CPU frequency translation statistics details
+
+
+"CPU Frequency scaling" (CONFIG_CPU_FREQ) should be enabled to configure
+cpufreq-stats.
+
+"CPU frequency translation statistics" (CONFIG_CPU_FREQ_STAT) provides the
+basic statistics which includes time_in_state and total_trans.
+
+"CPU frequency translation statistics details" (CONFIG_CPU_FREQ_STAT_DETAILS)
+provides fine grained cpufreq stats by trans_table. The reason for having a
+seperate config option for trans_table is:
+- trans_table goes against the traditional /sysfs rule of one value per
+  interface. It provides a whole bunch of value in a 2 dimensional matrix
+  form.
+
+Once these two options are enabled and your CPU supports cpufrequency, you
+will be able to see the CPU frequency statistics in /sysfs.
+
+
+
+
diff --git a/Documentation/cpusets.txt b/Documentation/cpusets.txt
new file mode 100644 (file)
index 0000000..2f8f24e
--- /dev/null
@@ -0,0 +1,414 @@
+                               CPUSETS
+                               -------
+
+Copyright (C) 2004 BULL SA.
+Written by Simon.Derr@bull.net
+
+Portions Copyright (c) 2004 Silicon Graphics, Inc.
+Modified by Paul Jackson <pj@sgi.com>
+
+CONTENTS:
+=========
+
+1. Cpusets
+  1.1 What are cpusets ?
+  1.2 Why are cpusets needed ?
+  1.3 How are cpusets implemented ?
+  1.4 How do I use cpusets ?
+2. Usage Examples and Syntax
+  2.1 Basic Usage
+  2.2 Adding/removing cpus
+  2.3 Setting flags
+  2.4 Attaching processes
+3. Questions
+4. Contact
+
+1. Cpusets
+==========
+
+1.1 What are cpusets ?
+----------------------
+
+Cpusets provide a mechanism for assigning a set of CPUs and Memory
+Nodes to a set of tasks.
+
+Cpusets constrain the CPU and Memory placement of tasks to only
+the resources within a tasks current cpuset.  They form a nested
+hierarchy visible in a virtual file system.  These are the essential
+hooks, beyond what is already present, required to manage dynamic
+job placement on large systems.
+
+Each task has a pointer to a cpuset.  Multiple tasks may reference
+the same cpuset.  Requests by a task, using the sched_setaffinity(2)
+system call to include CPUs in its CPU affinity mask, and using the
+mbind(2) and set_mempolicy(2) system calls to include Memory Nodes
+in its memory policy, are both filtered through that tasks cpuset,
+filtering out any CPUs or Memory Nodes not in that cpuset.  The
+scheduler will not schedule a task on a CPU that is not allowed in
+its cpus_allowed vector, and the kernel page allocator will not
+allocate a page on a node that is not allowed in the requesting tasks
+mems_allowed vector.
+
+If a cpuset is cpu or mem exclusive, no other cpuset, other than a direct
+ancestor or descendent, may share any of the same CPUs or Memory Nodes.
+
+User level code may create and destroy cpusets by name in the cpuset
+virtual file system, manage the attributes and permissions of these
+cpusets and which CPUs and Memory Nodes are assigned to each cpuset,
+specify and query to which cpuset a task is assigned, and list the
+task pids assigned to a cpuset.
+
+
+1.2 Why are cpusets needed ?
+----------------------------
+
+The management of large computer systems, with many processors (CPUs),
+complex memory cache hierarchies and multiple Memory Nodes having
+non-uniform access times (NUMA) presents additional challenges for
+the efficient scheduling and memory placement of processes.
+
+Frequently more modest sized systems can be operated with adequate
+efficiency just by letting the operating system automatically share
+the available CPU and Memory resources amongst the requesting tasks.
+
+But larger systems, which benefit more from careful processor and
+memory placement to reduce memory access times and contention,
+and which typically represent a larger investment for the customer,
+can benefit from explictly placing jobs on properly sized subsets of
+the system.
+
+This can be especially valuable on:
+
+    * Web Servers running multiple instances of the same web application,
+    * Servers running different applications (for instance, a web server
+      and a database), or
+    * NUMA systems running large HPC applications with demanding
+      performance characteristics.
+
+These subsets, or "soft partitions" must be able to be dynamically
+adjusted, as the job mix changes, without impacting other concurrently
+executing jobs.
+
+The kernel cpuset patch provides the minimum essential kernel
+mechanisms required to efficiently implement such subsets.  It
+leverages existing CPU and Memory Placement facilities in the Linux
+kernel to avoid any additional impact on the critical scheduler or
+memory allocator code.
+
+
+1.3 How are cpusets implemented ?
+---------------------------------
+
+Cpusets provide a Linux kernel (2.6.7 and above) mechanism to constrain
+which CPUs and Memory Nodes are used by a process or set of processes.
+
+The Linux kernel already has a pair of mechanisms to specify on which
+CPUs a task may be scheduled (sched_setaffinity) and on which Memory
+Nodes it may obtain memory (mbind, set_mempolicy).
+
+Cpusets extends these two mechanisms as follows:
+
+ - Cpusets are sets of allowed CPUs and Memory Nodes, known to the
+   kernel.
+ - Each task in the system is attached to a cpuset, via a pointer
+   in the task structure to a reference counted cpuset structure.
+ - Calls to sched_setaffinity are filtered to just those CPUs
+   allowed in that tasks cpuset.
+ - Calls to mbind and set_mempolicy are filtered to just
+   those Memory Nodes allowed in that tasks cpuset.
+ - The root cpuset contains all the systems CPUs and Memory
+   Nodes.
+ - For any cpuset, one can define child cpusets containing a subset
+   of the parents CPU and Memory Node resources.
+ - The hierarchy of cpusets can be mounted at /dev/cpuset, for
+   browsing and manipulation from user space.
+ - A cpuset may be marked exclusive, which ensures that no other
+   cpuset (except direct ancestors and descendents) may contain
+   any overlapping CPUs or Memory Nodes.
+ - You can list all the tasks (by pid) attached to any cpuset.
+
+The implementation of cpusets requires a few, simple hooks
+into the rest of the kernel, none in performance critical paths:
+
+ - in main/init.c, to initialize the root cpuset at system boot.
+ - in fork and exit, to attach and detach a task from its cpuset.
+ - in sched_setaffinity, to mask the requested CPUs by what's
+   allowed in that tasks cpuset.
+ - in sched.c migrate_all_tasks(), to keep migrating tasks within
+   the CPUs allowed by their cpuset, if possible.
+ - in the mbind and set_mempolicy system calls, to mask the requested
+   Memory Nodes by what's allowed in that tasks cpuset.
+ - in page_alloc, to restrict memory to allowed nodes.
+ - in vmscan.c, to restrict page recovery to the current cpuset.
+
+In addition a new file system, of type "cpuset" may be mounted,
+typically at /dev/cpuset, to enable browsing and modifying the cpusets
+presently known to the kernel.  No new system calls are added for
+cpusets - all support for querying and modifying cpusets is via
+this cpuset file system.
+
+Each task under /proc has an added file named 'cpuset', displaying
+the cpuset name, as the path relative to the root of the cpuset file
+system.
+
+The /proc/<pid>/status file for each task has two added lines,
+displaying the tasks cpus_allowed (on which CPUs it may be scheduled)
+and mems_allowed (on which Memory Nodes it may obtain memory),
+in the format seen in the following example:
+
+  Cpus_allowed:   ffffffff,ffffffff,ffffffff,ffffffff
+  Mems_allowed:   ffffffff,ffffffff
+
+Each cpuset is represented by a directory in the cpuset file system
+containing the following files describing that cpuset:
+
+ - cpus: list of CPUs in that cpuset
+ - mems: list of Memory Nodes in that cpuset
+ - cpu_exclusive flag: is cpu placement exclusive?
+ - mem_exclusive flag: is memory placement exclusive?
+ - tasks: list of tasks (by pid) attached to that cpuset
+
+New cpusets are created using the mkdir system call or shell
+command.  The properties of a cpuset, such as its flags, allowed
+CPUs and Memory Nodes, and attached tasks, are modified by writing
+to the appropriate file in that cpusets directory, as listed above.
+
+The named hierarchical structure of nested cpusets allows partitioning
+a large system into nested, dynamically changeable, "soft-partitions".
+
+The attachment of each task, automatically inherited at fork by any
+children of that task, to a cpuset allows organizing the work load
+on a system into related sets of tasks such that each set is constrained
+to using the CPUs and Memory Nodes of a particular cpuset.  A task
+may be re-attached to any other cpuset, if allowed by the permissions
+on the necessary cpuset file system directories.
+
+Such management of a system "in the large" integrates smoothly with
+the detailed placement done on individual tasks and memory regions
+using the sched_setaffinity, mbind and set_mempolicy system calls.
+
+The following rules apply to each cpuset:
+
+ - Its CPUs and Memory Nodes must be a subset of its parents.
+ - It can only be marked exclusive if its parent is.
+ - If its cpu or memory is exclusive, they may not overlap any sibling.
+
+These rules, and the natural hierarchy of cpusets, enable efficient
+enforcement of the exclusive guarantee, without having to scan all
+cpusets every time any of them change to ensure nothing overlaps a
+exclusive cpuset.  Also, the use of a Linux virtual file system (vfs)
+to represent the cpuset hierarchy provides for a familiar permission
+and name space for cpusets, with a minimum of additional kernel code.
+
+1.4 How do I use cpusets ?
+--------------------------
+
+In order to minimize the impact of cpusets on critical kernel
+code, such as the scheduler, and due to the fact that the kernel
+does not support one task updating the memory placement of another
+task directly, the impact on a task of changing its cpuset CPU
+or Memory Node placement, or of changing to which cpuset a task
+is attached, is subtle.
+
+If a cpuset has its Memory Nodes modified, then for each task attached
+to that cpuset, the next time that the kernel attempts to allocate
+a page of memory for that task, the kernel will notice the change
+in the tasks cpuset, and update its per-task memory placement to
+remain within the new cpusets memory placement.  If the task was using
+mempolicy MPOL_BIND, and the nodes to which it was bound overlap with
+its new cpuset, then the task will continue to use whatever subset
+of MPOL_BIND nodes are still allowed in the new cpuset.  If the task
+was using MPOL_BIND and now none of its MPOL_BIND nodes are allowed
+in the new cpuset, then the task will be essentially treated as if it
+was MPOL_BIND bound to the new cpuset (even though its numa placement,
+as queried by get_mempolicy(), doesn't change).  If a task is moved
+from one cpuset to another, then the kernel will adjust the tasks
+memory placement, as above, the next time that the kernel attempts
+to allocate a page of memory for that task.
+
+If a cpuset has its CPUs modified, then each task using that
+cpuset does _not_ change its behavior automatically.  In order to
+minimize the impact on the critical scheduling code in the kernel,
+tasks will continue to use their prior CPU placement until they
+are rebound to their cpuset, by rewriting their pid to the 'tasks'
+file of their cpuset.  If a task had been bound to some subset of its
+cpuset using the sched_setaffinity() call, and if any of that subset
+is still allowed in its new cpuset settings, then the task will be
+restricted to the intersection of the CPUs it was allowed on before,
+and its new cpuset CPU placement.  If, on the other hand, there is
+no overlap between a tasks prior placement and its new cpuset CPU
+placement, then the task will be allowed to run on any CPU allowed
+in its new cpuset.  If a task is moved from one cpuset to another,
+its CPU placement is updated in the same way as if the tasks pid is
+rewritten to the 'tasks' file of its current cpuset.
+
+In summary, the memory placement of a task whose cpuset is changed is
+updated by the kernel, on the next allocation of a page for that task,
+but the processor placement is not updated, until that tasks pid is
+rewritten to the 'tasks' file of its cpuset.  This is done to avoid
+impacting the scheduler code in the kernel with a check for changes
+in a tasks processor placement.
+
+There is an exception to the above.  If hotplug funtionality is used
+to remove all the CPUs that are currently assigned to a cpuset,
+then the kernel will automatically update the cpus_allowed of all
+tasks attached to CPUs in that cpuset to allow all CPUs.  When memory
+hotplug functionality for removing Memory Nodes is available, a
+similar exception is expected to apply there as well.  In general,
+the kernel prefers to violate cpuset placement, over starving a task
+that has had all its allowed CPUs or Memory Nodes taken offline.  User
+code should reconfigure cpusets to only refer to online CPUs and Memory
+Nodes when using hotplug to add or remove such resources.
+
+There is a second exception to the above.  GFP_ATOMIC requests are
+kernel internal allocations that must be satisfied, immediately.
+The kernel may drop some request, in rare cases even panic, if a
+GFP_ATOMIC alloc fails.  If the request cannot be satisfied within
+the current tasks cpuset, then we relax the cpuset, and look for
+memory anywhere we can find it.  It's better to violate the cpuset
+than stress the kernel.
+
+To start a new job that is to be contained within a cpuset, the steps are:
+
+ 1) mkdir /dev/cpuset
+ 2) mount -t cpuset none /dev/cpuset
+ 3) Create the new cpuset by doing mkdir's and write's (or echo's) in
+    the /dev/cpuset virtual file system.
+ 4) Start a task that will be the "founding father" of the new job.
+ 5) Attach that task to the new cpuset by writing its pid to the
+    /dev/cpuset tasks file for that cpuset.
+ 6) fork, exec or clone the job tasks from this founding father task.
+
+For example, the following sequence of commands will setup a cpuset
+named "Charlie", containing just CPUs 2 and 3, and Memory Node 1,
+and then start a subshell 'sh' in that cpuset:
+
+  mount -t cpuset none /dev/cpuset
+  cd /dev/cpuset
+  mkdir Charlie
+  cd Charlie
+  /bin/echo 2-3 > cpus
+  /bin/echo 1 > mems
+  /bin/echo $$ > tasks
+  sh
+  # The subshell 'sh' is now running in cpuset Charlie
+  # The next line should display '/Charlie'
+  cat /proc/self/cpuset
+
+In the case that a change of cpuset includes wanting to move already
+allocated memory pages, consider further the work of IWAMOTO
+Toshihiro <iwamoto@valinux.co.jp> for page remapping and memory
+hotremoval, which can be found at:
+
+  http://people.valinux.co.jp/~iwamoto/mh.html
+
+The integration of cpusets with such memory migration is not yet
+available.
+
+In the future, a C library interface to cpusets will likely be
+available.  For now, the only way to query or modify cpusets is
+via the cpuset file system, using the various cd, mkdir, echo, cat,
+rmdir commands from the shell, or their equivalent from C.
+
+The sched_setaffinity calls can also be done at the shell prompt using
+SGI's runon or Robert Love's taskset.  The mbind and set_mempolicy
+calls can be done at the shell prompt using the numactl command
+(part of Andi Kleen's numa package).
+
+2. Usage Examples and Syntax
+============================
+
+2.1 Basic Usage
+---------------
+
+Creating, modifying, using the cpusets can be done through the cpuset
+virtual filesystem.
+
+To mount it, type:
+# mount -t cpuset none /dev/cpuset
+
+Then under /dev/cpuset you can find a tree that corresponds to the
+tree of the cpusets in the system. For instance, /dev/cpuset
+is the cpuset that holds the whole system.
+
+If you want to create a new cpuset under /dev/cpuset:
+# cd /dev/cpuset
+# mkdir my_cpuset
+
+Now you want to do something with this cpuset.
+# cd my_cpuset
+
+In this directory you can find several files:
+# ls
+cpus  cpu_exclusive  mems  mem_exclusive  tasks
+
+Reading them will give you information about the state of this cpuset:
+the CPUs and Memory Nodes it can use, the processes that are using
+it, its properties.  By writing to these files you can manipulate
+the cpuset.
+
+Set some flags:
+# /bin/echo 1 > cpu_exclusive
+
+Add some cpus:
+# /bin/echo 0-7 > cpus
+
+Now attach your shell to this cpuset:
+# /bin/echo $$ > tasks
+
+You can also create cpusets inside your cpuset by using mkdir in this
+directory.
+# mkdir my_sub_cs
+
+To remove a cpuset, just use rmdir:
+# rmdir my_sub_cs
+This will fail if the cpuset is in use (has cpusets inside, or has
+processes attached).
+
+2.2 Adding/removing cpus
+------------------------
+
+This is the syntax to use when writing in the cpus or mems files
+in cpuset directories:
+
+# /bin/echo 1-4 > cpus         -> set cpus list to cpus 1,2,3,4
+# /bin/echo 1,2,3,4 > cpus     -> set cpus list to cpus 1,2,3,4
+
+2.3 Setting flags
+-----------------
+
+The syntax is very simple:
+
+# /bin/echo 1 > cpu_exclusive  -> set flag 'cpu_exclusive'
+# /bin/echo 0 > cpu_exclusive  -> unset flag 'cpu_exclusive'
+
+2.4 Attaching processes
+-----------------------
+
+# /bin/echo PID > tasks
+
+Note that it is PID, not PIDs. You can only attach ONE task at a time.
+If you have several tasks to attach, you have to do it one after another:
+
+# /bin/echo PID1 > tasks
+# /bin/echo PID2 > tasks
+       ...
+# /bin/echo PIDn > tasks
+
+
+3. Questions
+============
+
+Q: what's up with this '/bin/echo' ?
+A: bash's builtin 'echo' command does not check calls to write() against
+   errors. If you use it in the cpuset file system, you won't be
+   able to tell whether a command succeeded or failed.
+
+Q: When I attach processes, only the first of the line gets really attached !
+A: We can only return one error code per call to write(). So you should also
+   put only ONE pid.
+
+4. Contact
+==========
+
+Web: http://www.bullopensource.org/cpuset
diff --git a/Documentation/dontdiff b/Documentation/dontdiff
new file mode 100644 (file)
index 0000000..9a33bb9
--- /dev/null
@@ -0,0 +1,140 @@
+*.a
+*.aux
+*.bin
+*.cpio
+*.css
+*.dvi
+*.eps
+*.gif
+*.grep
+*.grp
+*.gz
+*.html
+*.jpeg
+*.ko
+*.log
+*.lst
+*.mod.c
+*.o
+*.orig
+*.out
+*.pdf
+*.png
+*.ps
+*.rej
+*.s
+*.sgml
+*.so
+*.tex
+*.ver
+*.xml
+*_MODULES
+*_vga16.c
+*cscope*
+*~
+.*
+.cscope
+53c700_d.h
+53c8xx_d.h*
+BitKeeper
+COPYING
+CREDITS
+CVS
+ChangeSet
+Kerntypes
+MODS.txt
+Module.symvers
+PENDING
+SCCS
+System.map*
+TAGS
+aic7*reg.h*
+aic7*reg_print.c*
+aic7*seq.h*
+aicasm
+aicdb.h*
+asm
+asm_offsets.*
+autoconf.h*
+bbootsect
+bin2c
+binkernel.spec
+bootsect
+bsetup
+btfixupprep
+build
+bvmlinux
+bzImage*
+classlist.h*
+comp*.log
+compile.h*
+config
+config-*
+config_data.h*
+conmakehash
+consolemap_deftbl.c*
+crc32table.h*
+cscope.*
+defkeymap.c*
+devlist.h*
+docproc
+dummy_sym.c*
+elfconfig.h*
+filelist
+fixdep
+fore200e_mkfirm
+fore200e_pca_fw.c*
+gen-devlist
+gen-kdb_cmds.c*
+gen_crc32table
+gen_init_cpio
+genksyms
+gentbl
+ikconfig.h*
+initramfs_list
+kallsyms
+kconfig
+kconfig.tk
+keywords.c*
+ksym.c*
+ksym.h*
+lex.c*
+logo_*.c
+logo_*_clut224.c
+logo_*_mono.c
+lxdialog
+make_times_h
+map
+maui_boot.h
+mk_elfconfig
+mkdep
+mktables
+modpost
+modversions.h*
+offsets.h
+oui.c*
+parse.c*
+parse.h*
+pnmtologo
+ppc_defs.h*
+promcon_tbl.c*
+pss_boot.h
+raid6altivec*.c
+raid6int*.c
+raid6tables.c
+setup
+sim710_d.h*
+sm_tbl*
+split-include
+tags
+times.h*
+tkparse
+trix_boot.h
+version.h*
+vmlinux
+vmlinux-*
+vmlinux.lds
+vsyscall.lds
+wanxlfw.inc
+uImage
+zImage
diff --git a/Documentation/dvb/ci.txt b/Documentation/dvb/ci.txt
new file mode 100644 (file)
index 0000000..62e0701
--- /dev/null
@@ -0,0 +1,219 @@
+* For the user
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+NOTE: This document describes the usage of the high level CI API as
+in accordance to the Linux DVB API. This is a not a documentation for the,
+existing low level CI API.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To utilize the High Level CI capabilities,
+
+(1*) This point is valid only for the Twinhan/clones
+  For the Twinhan/Twinhan clones, the dst_ca module handles the CI
+  hardware handling.This module is loaded automatically if a CI
+  (Common Interface, that holds the CAM (Conditional Access Module)
+  is detected.
+
+(2) one requires a userspace application, ca_zap. This small userland
+  application is in charge of sending the descrambling related information
+  to the CAM.
+
+This application requires the following to function properly as of now.
+
+       (a) Tune to a valid channel, with szap.
+         eg: $ szap -c channels.conf -r "TMC" -x
+
+       (b) a channels.conf containing a valid PMT PID
+
+         eg: TMC:11996:h:0:27500:278:512:650:321
+
+         here 278 is a valid PMT PID. the rest of the values are the
+         same ones that szap uses.
+
+       (c) after running a szap, you have to run ca_zap, for the
+         descrambler to function,
+
+         eg: $ ca_zap patched_channels.conf "TMC"
+
+         The patched means a patch to apply to scan, such that scan can
+         generate a channels.conf_with pmt, which has this PMT PID info
+         (NOTE: szap cannot use this channels.conf with the PMT_PID)
+
+
+       (d) Hopeflly Enjoy your favourite subscribed channel as you do with
+         a FTA card.
+
+(3) Currently ca_zap, and dst_test, both are meant for demonstration
+  purposes only, they can become full fledged applications if necessary.
+
+
+* Cards that fall in this category
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+At present the cards that fall in this category are the Twinhan and it's
+clones, these cards are available as VVMER, Tomato, Hercules, Orange and
+so on.
+
+* CI modules that are supported
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The CI module support is largely dependant upon the firmware on the cards
+Some cards do support almost all of the available CI modules. There is
+nothing much that can be done in order to make additional CI modules
+working with these cards.
+
+Modules that have been tested by this driver at present are
+
+(1) Irdeto 1 and 2 from SCM
+(2) Viaccess from SCM
+(3) Dragoncam
+
+* The High level CI API
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+* For the programmer
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+With the High Level CI approach any new card with almost any random
+architecture can be implemented with this style, the definitions
+insidethe switch statement can be easily adapted for any card, thereby
+eliminating the need for any additional ioctls.
+
+The disadvantage is that the driver/hardware has to manage the rest. For
+the application programmer it would be as simple as sending/receiving an
+array to/from the CI ioctls as defined in the Linux DVB API. No changes
+have been made in the API to accomodate this feature.
+
+
+* Why the need for another CI interface ?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This is one of the most commonly asked question. Well a nice question.
+Strictly speaking this is not a new interface.
+
+The CI interface is defined in the DVB API in ca.h as
+
+typedef struct ca_slot_info {
+       int num;               /* slot number */
+
+       int type;              /* CA interface this slot supports */
+#define CA_CI            1     /* CI high level interface */
+#define CA_CI_LINK       2     /* CI link layer level interface */
+#define CA_CI_PHYS       4     /* CI physical layer level interface */
+#define CA_DESCR         8     /* built-in descrambler */
+#define CA_SC          128     /* simple smart card interface */
+
+       unsigned int flags;
+#define CA_CI_MODULE_PRESENT 1 /* module (or card) inserted */
+#define CA_CI_MODULE_READY   2
+} ca_slot_info_t;
+
+
+
+This CI interface follows the CI high level interface, which is not
+implemented by most applications. Hence this area is revisited.
+
+This CI interface is quite different in the case that it tries to
+accomodate all other CI based devices, that fall into the other categories
+
+This means that this CI interface handles the EN50221 style tags in the
+Application layer only and no session management is taken care of by the
+application. The driver/hardware will take care of all that.
+
+This interface is purely an EN50221 interface exchanging APDU's. This
+means that no session management, link layer or a transport layer do
+exist in this case in the application to driver communication. It is
+as simple as that. The driver/hardware has to take care of that.
+
+
+With this High Level CI interface, the interface can be defined with the
+regular ioctls.
+
+All these ioctls are also valid for the High level CI interface
+
+#define CA_RESET          _IO('o', 128)
+#define CA_GET_CAP        _IOR('o', 129, ca_caps_t)
+#define CA_GET_SLOT_INFO  _IOR('o', 130, ca_slot_info_t)
+#define CA_GET_DESCR_INFO _IOR('o', 131, ca_descr_info_t)
+#define CA_GET_MSG        _IOR('o', 132, ca_msg_t)
+#define CA_SEND_MSG       _IOW('o', 133, ca_msg_t)
+#define CA_SET_DESCR      _IOW('o', 134, ca_descr_t)
+#define CA_SET_PID        _IOW('o', 135, ca_pid_t)
+
+
+On querying the device, the device yields information thus
+
+CA_GET_SLOT_INFO
+----------------------------
+Command = [info]
+APP: Number=[1]
+APP: Type=[1]
+APP: flags=[1]
+APP: CI High level interface
+APP: CA/CI Module Present
+
+CA_GET_CAP
+----------------------------
+Command = [caps]
+APP: Slots=[1]
+APP: Type=[1]
+APP: Descrambler keys=[16]
+APP: Type=[1]
+
+CA_SEND_MSG
+----------------------------
+Descriptors(Program Level)=[ 09 06 06 04 05 50 ff f1]
+Found CA descriptor @ program level
+
+(20) ES type=[2] ES pid=[201]  ES length =[0 (0x0)]
+(25) ES type=[4] ES pid=[301]  ES length =[0 (0x0)]
+ca_message length is 25 (0x19) bytes
+EN50221 CA MSG=[ 9f 80 32 19 03 01 2d d1 f0 08 01 09 06 06 04 05 50 ff f1 02 e0 c9 00 00 04 e1 2d 00 00]
+
+
+Not all ioctl's are implemented in the driver from the API, the other
+features of the hardware that cannot be implemented by the API are achieved
+using the CA_GET_MSG and CA_SEND_MSG ioctls. An EN50221 style wrapper is
+used to exchange the data to maintain compatibility with other hardware.
+
+
+/* a message to/from a CI-CAM */
+typedef struct ca_msg {
+       unsigned int index;
+       unsigned int type;
+       unsigned int length;
+       unsigned char msg[256];
+} ca_msg_t;
+
+
+The flow of data can be described thus,
+
+
+
+
+
+       App (User)
+       -----
+       parse
+         |
+         |
+         v
+       en50221 APDU (package)
+   --------------------------------------
+   |     |                             | High Level CI driver
+   |     |                             |
+   |     v                             |
+   |   en50221 APDU (unpackage)        |
+   |     |                             |
+   |     |                             |
+   |     v                             |
+   |   sanity checks                   |
+   |     |                             |
+   |     |                             |
+   |     v                             |
+   |   do (H/W dep)                    |
+   --------------------------------------
+         |    Hardware
+         |
+         v
+
+
+
+
+The High Level CI interface uses the EN50221 DVB standard, following a
+standard ensures futureproofness.
diff --git a/Documentation/i2c/busses/i2c-amd8111 b/Documentation/i2c/busses/i2c-amd8111
new file mode 100644 (file)
index 0000000..db294ee
--- /dev/null
@@ -0,0 +1,41 @@
+Kernel driver i2c-adm8111
+
+Supported adapters:
+    * AMD-8111 SMBus 2.0 PCI interface
+
+Datasheets:
+       AMD datasheet not yet available, but almost everything can be found
+       in publically available ACPI 2.0 specification, which the adapter 
+       follows.
+
+Author: Vojtech Pavlik <vojtech@suse.cz>
+
+Description
+-----------
+
+If you see something like this:
+
+00:07.2 SMBus: Advanced Micro Devices [AMD] AMD-8111 SMBus 2.0 (rev 02)
+        Subsystem: Advanced Micro Devices [AMD] AMD-8111 SMBus 2.0
+        Flags: medium devsel, IRQ 19
+        I/O ports at d400 [size=32]
+
+in your 'lspci -v', then this driver is for your chipset.
+
+Process Call Support
+--------------------
+
+Supported.
+
+SMBus 2.0 Support
+-----------------
+
+Supported. Both PEC and block process call support is implemented. Slave
+mode or host notification are not yet implemented.
+
+Notes
+-----
+
+Note that for the 8111, there are two SMBus adapters. The SMBus 2.0 adapter
+is supported by this driver, and the SMBus 1.0 adapter is supported by the
+i2c-amd756 driver.
diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801
new file mode 100644 (file)
index 0000000..fd4b271
--- /dev/null
@@ -0,0 +1,80 @@
+Kernel driver i2c-i801
+
+Supported adapters:
+  * Intel 82801AA and 82801AB (ICH and ICH0 - part of the
+    '810' and '810E' chipsets)
+  * Intel 82801BA (ICH2 - part of the '815E' chipset)
+  * Intel 82801CA/CAM (ICH3)
+  * Intel 82801DB (ICH4) (HW PEC supported, 32 byte buffer not supported)
+  * Intel 82801EB/ER (ICH5) (HW PEC supported, 32 byte buffer not supported)
+  * Intel 6300ESB
+  * Intel 82801FB/FR/FW/FRW (ICH6)
+  * Intel ICH7
+    Datasheets: Publicly available at the Intel website
+
+Authors: 
+       Frodo Looijaard <frodol@dds.nl>, 
+       Philip Edelbrock <phil@netroedge.com>, 
+       Mark Studebaker <mdsxyz123@yahoo.com>
+
+
+Module Parameters
+-----------------
+
+* force_addr: int
+  Forcibly enable the ICH at the given address. EXTREMELY DANGEROUS!
+
+
+Description
+-----------
+
+The ICH (properly known as the 82801AA), ICH0 (82801AB), ICH2 (82801BA),
+ICH3 (82801CA/CAM) and later devices are Intel chips that are a part of
+Intel's '810' chipset for Celeron-based PCs, '810E' chipset for
+Pentium-based PCs, '815E' chipset, and others.
+
+The ICH chips contain at least SEVEN separate PCI functions in TWO logical
+PCI devices. An output of lspci will show something similar to the
+following:
+
+  00:1e.0 PCI bridge: Intel Corporation: Unknown device 2418 (rev 01)
+  00:1f.0 ISA bridge: Intel Corporation: Unknown device 2410 (rev 01)
+  00:1f.1 IDE interface: Intel Corporation: Unknown device 2411 (rev 01)
+  00:1f.2 USB Controller: Intel Corporation: Unknown device 2412 (rev 01)
+  00:1f.3 Unknown class [0c05]: Intel Corporation: Unknown device 2413 (rev 01)
+
+The SMBus controller is function 3 in device 1f. Class 0c05 is SMBus Serial
+Controller.
+
+If you do NOT see the 24x3 device at function 3, and you can't figure out
+any way in the BIOS to enable it,
+
+The ICH chips are quite similar to Intel's PIIX4 chip, at least in the
+SMBus controller.
+
+See the file i2c-piix4 for some additional information.
+
+
+Process Call Support
+--------------------
+
+Not supported.
+
+
+I2C Block Read Support
+----------------------
+
+Not supported at the moment.
+
+
+SMBus 2.0 Support
+-----------------
+
+The 82801DB (ICH4) and later chips support several SMBus 2.0 features.
+
+**********************
+The lm_sensors project gratefully acknowledges the support of Texas
+Instruments in the initial development of this driver.
+
+The lm_sensors project gratefully acknowledges the support of Intel in the
+development of SMBus 2.0 / ICH4 features of this driver.
diff --git a/Documentation/i2c/busses/i2c-nforce2 b/Documentation/i2c/busses/i2c-nforce2
new file mode 100644 (file)
index 0000000..e379e18
--- /dev/null
@@ -0,0 +1,41 @@
+Kernel driver i2c-nforce2
+
+Supported adapters:
+  * nForce2 MCP                10de:0064 
+  * nForce2 Ultra 400 MCP      10de:0084 
+  * nForce3 Pro150 MCP         10de:00D4 
+  * nForce3 250Gb MCP          10de:00E4 
+  * nForce4 MCP                       10de:0052
+
+Datasheet: not publically available, but seems to be similar to the
+           AMD-8111 SMBus 2.0 adapter.
+
+Authors:
+       Hans-Frieder Vogt <hfvogt@arcor.de>, 
+       Thomas Leibold <thomas@plx.com>, 
+        Patrick Dreker <patrick@dreker.de>
+       
+Description
+-----------
+
+i2c-nforce2 is a driver for the SMBuses included in the nVidia nForce2 MCP.
+
+If your 'lspci -v' listing shows something like the following,
+
+00:01.1 SMBus: nVidia Corporation: Unknown device 0064 (rev a2)
+        Subsystem: Asustek Computer, Inc.: Unknown device 0c11
+        Flags: 66Mhz, fast devsel, IRQ 5
+        I/O ports at c000 [size=32]
+        Capabilities: <available only to root>
+
+then this driver should support the SMBuses of your motherboard.
+
+
+Notes
+-----
+
+The SMBus adapter in the nForce2 chipset seems to be very similar to the
+SMBus 2.0 adapter in the AMD-8111 southbridge. However, I could only get
+the driver to work with direct I/O access, which is different to the EC
+interface of the AMD-8111. Tested on Asus A7N8X. The ACPI DSDT table of the
+Asus A7N8X lists two SMBuses, both of which are supported by this driver.
diff --git a/Documentation/i2c/busses/i2c-parport b/Documentation/i2c/busses/i2c-parport
new file mode 100644 (file)
index 0000000..9f1d008
--- /dev/null
@@ -0,0 +1,154 @@
+Kernel driver i2c-parport
+
+Author: Jean Delvare <khali@linux-fr.org> 
+
+This is a unified driver for several i2c-over-parallel-port adapters,
+such as the ones made by Philips, Velleman or ELV. This driver is
+meant as a replacement for the older, individual drivers:
+ * i2c-philips-par
+ * i2c-elv
+ * i2c-velleman
+ * video/i2c-parport (NOT the same as this one, dedicated to home brew
+                      teletext adapters)
+
+It currently supports the following devices:
+ * Philips adapter
+ * home brew teletext adapter
+ * Velleman K8000 adapter
+ * ELV adapter
+ * Analog Devices evaluation boards (ADM1025, ADM1030, ADM1031, ADM1032)
+
+These devices use different pinout configurations, so you have to tell
+the driver what you have, using the type module parameter. There is no
+way to autodetect the devices. Support for different pinout configurations
+can be easily added when needed.
+
+
+Building your own adapter
+-------------------------
+
+If you want to build you own i2c-over-parallel-port adapter, here is
+a sample electronics schema (credits go to Sylvain Munaut):
+
+Device                                                      PC
+Side          ___________________Vdd (+)                    Side
+               |    |         |
+              ---  ---       ---
+              | |  | |       | |
+              |R|  |R|       |R|
+              | |  | |       | |
+              ---  ---       ---
+               |    |         |
+               |    |    /|   |
+SCL  ----------x--------o |-----------x-------------------  pin 2
+                    |    \|   |       |
+                    |         |       |
+                    |   |\    |       |
+SDA  ----------x----x---| o---x---------------------------  pin 13
+               |        |/            |
+               |                      |
+               |         /|           |
+               ---------o |----------------x--------------  pin 3
+                         \|           |    |
+                                      |    |
+                                     ---  ---
+                                     | |  | |
+                                     |R|  |R|
+                                     | |  | |
+                                     ---  ---
+                                      |    | 
+                                     ###  ###
+                                     GND  GND
+        
+Remarks:
+ - This is the exact pinout and electronics used on the Analog Devices
+   evaluation boards.
+                   /|
+ - All inverters -o |- must be 74HC05, they must be open collector output.
+                   \|
+ - All resitors are 10k.
+ - Pins 18-25 of the parallel port connected to GND.
+ - Pins 4-9 (D2-D7) could be used as VDD is the driver drives them high.
+   The ADM1032 evaluation board uses D4-D7. Beware that the amount of
+   current you can draw from the parallel port is limited. Also note that
+   all connected lines MUST BE driven at the same state, else you'll short
+   circuit the output buffers! So plugging the I2C adapter after loading
+   the i2c-parport module might be a good safety since data line state
+   prior to init may be unknown. 
+ - This is 5V!
+ - Obviously you cannot read SCL (so it's not really standard-compliant).
+   Pretty easy to add, just copy the SDA part and use another input pin.
+   That would give (ELV compatible pinout):
+
+
+Device                                                      PC
+Side          ______________________________Vdd (+)         Side
+               |    |            |    |
+              ---  ---          ---  ---
+              | |  | |          | |  | |
+              |R|  |R|          |R|  |R|
+              | |  | |          | |  | |
+              ---  ---          ---  ---
+               |    |            |    |
+               |    |      |\    |    |
+SCL  ----------x--------x--| o---x------------------------  pin 15
+                    |   |  |/         | 
+                    |   |             |
+                    |   |   /|        |
+                    |   ---o |-------------x--------------  pin 2
+                    |       \|        |    |
+                    |                 |    |
+                    |                 |    |
+                    |      |\         |    |
+SDA  ---------------x---x--| o--------x-------------------  pin 10
+                        |  |/              |
+                        |                  |
+                        |   /|             |
+                        ---o |------------------x---------  pin 3
+                            \|             |    |
+                                           |    |
+                                          ---  ---
+                                          | |  | |
+                                          |R|  |R|
+                                          | |  | |
+                                          ---  ---
+                                           |    | 
+                                          ###  ###
+                                          GND  GND
+
+
+If possible, you should use the same pinout configuration as existing
+adapters do, so you won't even have to change the code.
+
+
+Similar (but different) drivers
+-------------------------------
+
+This driver is NOT the same as the i2c-pport driver found in the i2c
+package. The i2c-pport driver makes use of modern parallel port features so
+that you don't need additional electronics. It has other restrictions
+however, and was not ported to Linux 2.6 (yet).
+
+This driver is also NOT the same as the i2c-pcf-epp driver found in the
+lm_sensors package. The i2c-pcf-epp driver doesn't use the parallel port as
+an I2C bus directly. Instead, it uses it to control an external I2C bus
+master. That driver was not ported to Linux 2.6 (yet) either.
+
+
+Legacy documentation for Velleman adapter
+-----------------------------------------
+
+Useful links:
+Velleman                http://www.velleman.be/
+Velleman K8000 Howto    http://howto.htlw16.ac.at/k8000-howto.html
+
+The project has lead to new libs for the Velleman K8000 and K8005:
+  LIBK8000 v1.99.1 and LIBK8005 v0.21
+With these libs, you can control the K8000 interface card and the K8005
+stepper motor card with the simple commands which are in the original
+Velleman software, like SetIOchannel, ReadADchannel, SendStepCCWFull and
+many more, using /dev/velleman.
+  http://home.wanadoo.nl/hihihi/libk8000.htm
+  http://home.wanadoo.nl/hihihi/libk8005.htm
+  http://struyve.mine.nu:8080/index.php?block=k8000
+  http://sourceforge.net/projects/libk8005/
diff --git a/Documentation/i2c/busses/i2c-piix4 b/Documentation/i2c/busses/i2c-piix4
new file mode 100644 (file)
index 0000000..856b4b8
--- /dev/null
@@ -0,0 +1,72 @@
+Kernel driver i2c-piix4
+
+Supported adapters:
+  * Intel 82371AB PIIX4 and PIIX4E
+  * Intel 82443MX (440MX)
+    Datasheet: Publicly available at the Intel website
+  * ServerWorks OSB4, CSB5 and CSB6 southbridges
+    Datasheet: Only available via NDA from ServerWorks
+  * Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge
+    Datasheet: Publicly available at the SMSC website http://www.smsc.com
+
+Authors: 
+       Frodo Looijaard <frodol@dds.nl>
+       Philip Edelbrock <phil@netroedge.com>
+
+
+Module Parameters
+-----------------
+
+* force: int
+  Forcibly enable the PIIX4. DANGEROUS!
+* force_addr: int
+  Forcibly enable the PIIX4 at the given address. EXTREMELY DANGEROUS!
+* fix_hstcfg: int
+  Fix config register. Needed on some boards (Force CPCI735).
+
+
+Description
+-----------
+
+The PIIX4 (properly known as the 82371AB) is an Intel chip with a lot of
+functionality. Among other things, it implements the PCI bus. One of its
+minor functions is implementing a System Management Bus. This is a true 
+SMBus - you can not access it on I2C levels. The good news is that it
+natively understands SMBus commands and you do not have to worry about
+timing problems. The bad news is that non-SMBus devices connected to it can
+confuse it mightily. Yes, this is known to happen...
+
+Do 'lspci -v' and see whether it contains an entry like this:
+
+0000:00:02.3 Bridge: Intel Corp. 82371AB/EB/MB PIIX4 ACPI (rev 02)
+            Flags: medium devsel, IRQ 9
+
+Bus and device numbers may differ, but the function number must be
+identical (like many PCI devices, the PIIX4 incorporates a number of
+different 'functions', which can be considered as separate devices). If you
+find such an entry, you have a PIIX4 SMBus controller.
+
+On some computers (most notably, some Dells), the SMBus is disabled by
+default. If you use the insmod parameter 'force=1', the kernel module will
+try to enable it. THIS IS VERY DANGEROUS! If the BIOS did not set up a
+correct address for this module, you could get in big trouble (read:
+crashes, data corruption, etc.). Try this only as a last resort (try BIOS
+updates first, for example), and backup first! An even more dangerous
+option is 'force_addr=<IOPORT>'. This will not only enable the PIIX4 like
+'force' foes, but it will also set a new base I/O port address. The SMBus
+parts of the PIIX4 needs a range of 8 of these addresses to function
+correctly. If these addresses are already reserved by some other device,
+you will get into big trouble! DON'T USE THIS IF YOU ARE NOT VERY SURE
+ABOUT WHAT YOU ARE DOING!
+
+The PIIX4E is just an new version of the PIIX4; it is supported as well.
+The PIIX/PIIX3 does not implement an SMBus or I2C bus, so you can't use
+this driver on those mainboards.
+
+The ServerWorks Southbridges, the Intel 440MX, and the Victory766 are
+identical to the PIIX4 in I2C/SMBus support.
+
+A few OSB4 southbridges are known to be misconfigured by the BIOS. In this
+case, you have you use the fix_hstcfg module parameter. Do not use it
+unless you know you have to, because in some cases it also breaks
+configuration on southbridges that don't need it.
diff --git a/Documentation/i2c/busses/i2c-sis69x b/Documentation/i2c/busses/i2c-sis69x
new file mode 100644 (file)
index 0000000..5be4876
--- /dev/null
@@ -0,0 +1,73 @@
+Kernel driver i2c-sis96x
+
+Replaces 2.4.x i2c-sis645
+
+Supported adapters:
+  * Silicon Integrated Systems Corp (SiS)
+    Any combination of these host bridges:
+       645, 645DX (aka 646), 648, 650, 651, 655, 735, 745, 746
+    and these south bridges:
+       961, 962, 963(L) 
+
+Author: Mark M. Hoffman <mhoffman@lightlink.com>
+
+Description
+-----------
+
+This SMBus only driver is known to work on motherboards with the above
+named chipset combinations. The driver was developed without benefit of a
+proper datasheet from SiS. The SMBus registers are assumed compatible with
+those of the SiS630, although they are located in a completely different
+place. Thanks to Alexander Malysh <amalysh@web.de> for providing the
+SiS630 datasheet (and  driver).
+
+The command "lspci" as root should produce something like these lines:
+
+00:00.0 Host bridge: Silicon Integrated Systems [SiS]: Unknown device 0645
+00:02.0 ISA bridge: Silicon Integrated Systems [SiS] 85C503/5513
+00:02.1 SMBus: Silicon Integrated Systems [SiS]: Unknown device 0016
+
+or perhaps this...
+
+00:00.0 Host bridge: Silicon Integrated Systems [SiS]: Unknown device 0645 
+00:02.0 ISA bridge: Silicon Integrated Systems [SiS]: Unknown device 0961
+00:02.1 SMBus: Silicon Integrated Systems [SiS]: Unknown device 0016
+
+(kernel versions later than 2.4.18 may fill in the "Unknown"s)
+
+If you cant see it please look on quirk_sis_96x_smbus
+(drivers/pci/quirks.c) (also if southbridge detection fails)
+
+I suspect that this driver could be made to work for the following SiS
+chipsets as well: 635, and 635T. If anyone owns a board with those chips
+AND is willing to risk crashing & burning an otherwise well-behaved kernel
+in the name of progress... please contact me at <mhoffman@lightlink.com> or
+via the project's mailing list: <sensors@stimpy.netroedge.com>.  Please
+send bug reports and/or success stories as well.
+
+
+TO DOs
+------
+
+* The driver does not support SMBus block reads/writes; I may add them if a
+scenario is found where they're needed.
+
+
+Thank You
+---------
+
+Mark D. Studebaker <mdsxyz123@yahoo.com>
+ - design hints and bug fixes
+Alexander Maylsh <amalysh@web.de>
+ - ditto, plus an important datasheet... almost the one I really wanted
+Hans-Günter Lütke Uphues <hg_lu@t-online.de>
+ - patch for SiS735
+Robert Zwerus <arzie@dds.nl>
+ - testing for SiS645DX
+Kianusch Sayah Karadji <kianusch@sk-tech.net>
+ - patch for SiS645DX/962
+Ken Healy
+ - patch for SiS655
+
+To anyone else who has written w/ feedback, thanks!
+
diff --git a/Documentation/i2c/busses/i2c-viapro b/Documentation/i2c/busses/i2c-viapro
new file mode 100644 (file)
index 0000000..702f5ac
--- /dev/null
@@ -0,0 +1,47 @@
+Kernel driver i2c-viapro
+
+Supported adapters:
+  * VIA Technologies, Inc. VT82C596A/B
+    Datasheet: Sometimes available at the VIA website
+
+  * VIA Technologies, Inc. VT82C686A/B 
+    Datasheet: Sometimes available at the VIA website
+
+  * VIA Technologies, Inc. VT8231, VT8233, VT8233A, VT8235, VT8237
+    Datasheet: available on request from Via
+
+Authors:
+       Frodo Looijaard <frodol@dds.nl>,  
+       Philip Edelbrock <phil@netroedge.com>, 
+       Kyösti Mälkki <kmalkki@cc.hut.fi>, 
+       Mark D. Studebaker <mdsxyz123@yahoo.com> 
+
+Module Parameters
+-----------------
+
+* force: int
+  Forcibly enable the SMBus controller. DANGEROUS!
+* force_addr: int
+  Forcibly enable the SMBus at the given address. EXTREMELY DANGEROUS!
+
+Description
+-----------
+
+i2c-viapro is a true SMBus host driver for motherboards with one of the
+supported VIA southbridges.
+
+Your lspci -n listing must show one of these :
+
+ device 1106:3050   (VT82C596 function 3)
+ device 1106:3051   (VT82C596 function 3)
+ device 1106:3057   (VT82C686 function 4)
+ device 1106:3074   (VT8233)
+ device 1106:3147   (VT8233A)
+ device 1106:8235   (VT8231)
+ devide 1106:3177   (VT8235)
+ devide 1106:3227   (VT8237)
+
+If none of these show up, you should look in the BIOS for settings like
+enable ACPI / SMBus or even USB.
+
+
diff --git a/Documentation/i2c/busses/scx200_acb b/Documentation/i2c/busses/scx200_acb
new file mode 100644 (file)
index 0000000..08c8cd1
--- /dev/null
@@ -0,0 +1,14 @@
+Kernel driver scx200_acb
+
+Author: Christer Weinigel <wingel@nano-system.com>
+
+Module Parameters
+-----------------
+
+* base: int
+  Base addresses for the ACCESS.bus controllers
+
+Description
+-----------
+
+Enable the use of the ACCESS.bus controllers of a SCx200 processor.
diff --git a/Documentation/usb/usbmon.txt b/Documentation/usb/usbmon.txt
new file mode 100644 (file)
index 0000000..2f8431f
--- /dev/null
@@ -0,0 +1,156 @@
+* Introduction
+
+The name "usbmon" in lowercase refers to a facility in kernel which is
+used to collect traces of I/O on the USB bus. This function is analogous
+to a packet socket used by network monitoring tools such as tcpdump(1)
+or Ethereal. Similarly, it is expected that a tool such as usbdump or
+USBMon (with uppercase letters) is used to examine raw traces produced
+by usbmon.
+
+The usbmon reports requests made by peripheral-specific drivers to Host
+Controller Drivers (HCD). So, if HCD is buggy, the traces reported by
+usbmon may not correspond to bus transactions precisely. This is the same
+situation as with tcpdump.
+
+* How to use usbmon to collect raw text traces
+
+Unlike the packet socket, usbmon has an interface which provides traces
+in a text format. This is used for two purposes. First, it serves as a
+common trace exchange format for tools while most sophisticated formats
+are finalized. Second, humans can read it in case tools are not available.
+
+To collect a raw text trace, execute following steps.
+
+1. Prepare
+
+Mount debugfs (it has to be enabled in your kernel configuration), and
+load the usbmon module (if built as module). The second step is skipped
+if usbmon is built into the kernel.
+
+# mount -t debugfs none_debugs /sys/kernel/debug
+# modprobe usbmon
+
+Verify that bus sockets are present.
+
+[root@lembas zaitcev]# ls /sys/kernel/debug/usbmon
+1s  1t  2s  2t  3s  3t  4s  4t
+[root@lembas zaitcev]#
+
+# ls /sys/kernel
+
+2. Find which bus connects to the desired device
+
+Run "cat /proc/bus/usb/devices", and find the T-line which corresponds to
+the device. Usually you do it by looking for the vendor string. If you have
+many similar devices, unplug one and compare two /proc/bus/usb/devices outputs.
+The T-line will have a bus number. Example:
+
+T:  Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=12  MxCh= 0
+D:  Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
+P:  Vendor=0557 ProdID=2004 Rev= 1.00
+S:  Manufacturer=ATEN
+S:  Product=UC100KM V2.00
+
+Bus=03 means it's bus 3.
+
+3. Start 'cat'
+
+# cat /sys/kernel/debug/usbmon/3t > /tmp/1.mon.out
+
+This process will be reading until killed. Naturally, the output can be
+redirected to a desirable location. This is preferred, because it is going
+to be quite long.
+
+4. Perform the desired operation on the USB bus
+
+This is where you do something that creates the traffic: plug in a flash key,
+copy files, control a webcam, etc.
+
+5. Kill cat
+
+Usually it's done with a keyboard interrupt (Control-C).
+
+At this point the output file (/tmp/1.mon.out in this example) can be saved,
+sent by e-mail, or inspected with a text editor. In the last case make sure
+that the file size is not excessive for your favourite editor.
+
+* Raw text data format
+
+The '0t' type data consists of a stream of events, such as URB submission,
+URB callback, submission error. Every event is a text line, which consists
+of whitespace separated words. The number of position of words may depend
+on the event type, but there is a set of words, common for all types.
+
+Here is the list of words, from left to right:
+- URB Tag. This is used to identify URBs is normally a kernel mode address
+ of the URB structure in hexadecimal.
+- Timestamp in microseconds, a decimal number. The timestamp's resolution
+  depends on available clock, and so it can be much worse than a microsecond
+  (if the implementation uses jiffies, for example).
+- Event Type. This type refers to the format of the event, not URB type.
+  Available types are: S - submission, C - callback, E - submission error.
+- "Pipe". The pipe concept is deprecated. This is a composite word, used to
+  be derived from information in pipes. It consists of three fields, separated
+  by colons: URB type and direction, Device address, Endpoint number.
+  Type and direction are encoded with two bytes in the following manner:
+    Ci Co   Control input and output
+    Zi Zo   Isochronous input and output
+    Ii Io   Interrupt input and output
+    Bi Bo   Bulk input and output
+  Device address and Endpoint number are decimal numbers with leading zeroes
+  or 3 and 2 positions, correspondingly.
+- URB Status. This field makes no sense for submissions, but is present
+  to help scripts with parsing. In error case, it contains the error code.
+- Data Length. This is the actual length in the URB.
+- Data tag. The usbmon may not always capture data, even if length is nonzero.
+  Only if tag is '=', the data words are present.
+- Data words follow, in big endian hexadecimal format. Notice that they are
+  not machine words, but really just a byte stream split into words to make
+  it easier to read. Thus, the last word may contain from one to four bytes.
+  The length of collected data is limited and can be less than the data length
+  report in Data Length word.
+
+Here is an example of code to read the data stream in a well known programming
+language:
+
+class ParsedLine {
+       int data_len;           /* Available length of data */
+       byte data[];
+
+       void parseData(StringTokenizer st) {
+               int availwords = st.countTokens();
+               data = new byte[availwords * 4];
+               data_len = 0;
+               while (st.hasMoreTokens()) {
+                       String data_str = st.nextToken();
+                       int len = data_str.length() / 2;
+                       int i;
+                       for (i = 0; i < len; i++) {
+                               data[data_len] = Byte.parseByte(
+                                   data_str.substring(i*2, i*2 + 2),
+                                   16);
+                               data_len++;
+                       }
+               }
+       }
+}
+
+This format is obviously deficient. For example, the setup packet for control
+transfers is not delivered. This will change in the future.
+
+Examples:
+
+An input control transfer to get a port status:
+
+d74ff9a0 2640288196 S Ci:001:00 -115 4 <
+d74ff9a0 2640288202 C Ci:001:00 0 4 = 01010100
+
+An output bulk transfer to send a SCSI command 0x5E in a 31-byte Bulk wrapper
+to a storage device at address 5:
+
+dd65f0e8 4128379752 S Bo:005:02 -115 31 = 55534243 5e000000 00000000 00000600 00000000 00000000 00000000 000000
+dd65f0e8 4128379808 C Bo:005:02 0 31 >
+
+* Raw binary format and API
+
+TBD
diff --git a/arch/arm/configs/pxa255-idp_defconfig b/arch/arm/configs/pxa255-idp_defconfig
new file mode 100644 (file)
index 0000000..21c3278
--- /dev/null
@@ -0,0 +1,799 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.12-rc1-bk2
+# Sun Mar 27 22:20:17 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
+
+#
+# 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_HOTPLUG=y
+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_BASE_FULL=y
+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
+CONFIG_BASE_SMALL=0
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODULE_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=y
+# 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
+
+#
+# Intel PXA2xx Implementations
+#
+# CONFIG_ARCH_LUBBOCK is not set
+# CONFIG_MACH_MAINSTONE is not set
+CONFIG_ARCH_PXA_IDP=y
+# CONFIG_PXA_SHARPSL is not set
+CONFIG_PXA25x=y
+
+#
+# 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
+
+#
+# Bus support
+#
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+# CONFIG_PREEMPT is not set
+CONFIG_LEDS=y
+CONFIG_LEDS_TIMER=y
+CONFIG_LEDS_CPU=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="root=/dev/nfs ip=dhcp console=ttyS0,115200 mem=64M"
+# CONFIG_XIP_KERNEL is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_ARTHUR is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+
+#
+# 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=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AFS_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_GEOMETRY=y
+# CONFIG_MTD_MAP_BANK_WIDTH_1 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_2 is not set
+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 is not set
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_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
+# CONFIG_MTD_SHARP_SL 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
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT 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 is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_RAM is not set
+CONFIG_BLK_DEV_RAM_COUNT=16
+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 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 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 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 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=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
+
+#
+# Ethernet (1000 Mbit)
+#
+
+#
+# Ethernet (10000 Mbit)
+#
+
+#
+# Token Ring devices
+#
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_TSDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_GAMEPORT is not set
+CONFIG_SOUND_GAMEPORT=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 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_PXA_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# 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
+# CONFIG_RAW_DRIVER is not set
+
+#
+# TPM devices
+#
+# CONFIG_TCG_TPM is not set
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Misc devices
+#
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+# CONFIG_DVB is not set
+
+#
+# Graphics support
+#
+CONFIG_FB=y
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+CONFIG_FB_SOFT_CURSOR=y
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+CONFIG_FB_PXA=y
+# CONFIG_FB_PXA_PARAMETERS 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=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+CONFIG_USB_ARCH_HAS_HCD=y
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB is not set
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC 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
+
+#
+# XFS support
+#
+# 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_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=0
+# 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 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_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=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_DEBUG_KERNEL=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_LOG_BUF_SHIFT=14
+# 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_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 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/lib/bitops.h b/arch/arm/lib/bitops.h
new file mode 100644 (file)
index 0000000..4a83ab6
--- /dev/null
@@ -0,0 +1,33 @@
+       .macro  bitop, instr
+       and     r2, r0, #7
+       mov     r3, #1
+       mov     r3, r3, lsl r2
+       save_and_disable_irqs ip, r2
+       ldrb    r2, [r1, r0, lsr #3]
+       \instr  r2, r2, r3
+       strb    r2, [r1, r0, lsr #3]
+       restore_irqs ip
+       mov     pc, lr
+       .endm
+
+/**
+ * testop - implement a test_and_xxx_bit operation.
+ * @instr: operational instruction
+ * @store: store instruction
+ *
+ * Note: we can trivially conditionalise the store instruction
+ * to avoid dirting the data cache.
+ */
+       .macro  testop, instr, store
+       add     r1, r1, r0, lsr #3
+       and     r3, r0, #7
+       mov     r0, #1
+       save_and_disable_irqs ip, r2
+       ldrb    r2, [r1]
+       tst     r2, r0, lsl r3
+       \instr  r2, r2, r0, lsl r3
+       \store  r2, [r1]
+       restore_irqs ip
+       moveq   r0, #0
+       mov     pc, lr
+       .endm
diff --git a/arch/arm/mach-omap/board-netstar.c b/arch/arm/mach-omap/board-netstar.c
new file mode 100644 (file)
index 0000000..54acbd2
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Modified from board-generic.c
+ *
+ * Copyright (C) 2004 2N Telekomunikace, Ladislav Michl <michl@2n.cz>
+ *
+ * Code for Netstar OMAP board.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <asm/arch/gpio.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/usb.h>
+
+#include "common.h"
+
+extern void __init omap_init_time(void);
+extern int omap_gpio_init(void);
+
+static struct resource netstar_smc91x_resources[] = {
+       [0] = {
+               .start  = OMAP_CS1_PHYS + 0x300,
+               .end    = OMAP_CS1_PHYS + 0x300 + 16,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = OMAP_GPIO_IRQ(8),
+               .end    = OMAP_GPIO_IRQ(8),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device netstar_smc91x_device = {
+       .name           = "smc91x",
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(netstar_smc91x_resources),
+       .resource       = netstar_smc91x_resources,
+};
+
+static struct platform_device *netstar_devices[] __initdata = {
+       &netstar_smc91x_device,
+};
+
+static void __init netstar_init_irq(void)
+{
+       omap_init_irq();
+       omap_gpio_init();
+}
+
+static void __init netstar_init(void)
+{
+       /* green LED */
+       omap_request_gpio(4);
+       omap_set_gpio_direction(4, 0);
+       /* smc91x reset */
+       omap_request_gpio(7);
+       omap_set_gpio_direction(7, 0);
+       omap_set_gpio_dataout(7, 1);
+       udelay(2);      /* wait at least 100ns */
+       omap_set_gpio_dataout(7, 0);
+       mdelay(50);     /* 50ms until PHY ready */
+       /* smc91x interrupt pin */
+       omap_request_gpio(8);
+       omap_set_gpio_edge_ctrl(8, OMAP_GPIO_RISING_EDGE);
+
+       omap_request_gpio(12);
+       omap_request_gpio(13);
+       omap_request_gpio(14);
+       omap_request_gpio(15);
+       omap_set_gpio_edge_ctrl(12, OMAP_GPIO_FALLING_EDGE);
+       omap_set_gpio_edge_ctrl(13, OMAP_GPIO_FALLING_EDGE);
+       omap_set_gpio_edge_ctrl(14, OMAP_GPIO_FALLING_EDGE);
+       omap_set_gpio_edge_ctrl(15, OMAP_GPIO_FALLING_EDGE);
+
+       platform_add_devices(netstar_devices, ARRAY_SIZE(netstar_devices));
+
+       /* Switch on green LED */
+       omap_set_gpio_dataout(4, 0);
+       /* Switch off red LED */
+       omap_writeb(0x00, OMAP_LPG1_PMR);       /* Disable clock */
+       omap_writeb(0x80, OMAP_LPG1_LCR);
+}
+
+static int __initdata omap_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1};
+
+static void __init netstar_map_io(void)
+{
+       omap_map_io();
+       omap_serial_init(omap_serial_ports);
+}
+
+#define MACHINE_PANICED                1
+#define MACHINE_REBOOTING      2
+#define MACHINE_REBOOT         4
+static unsigned long machine_state;
+
+static int panic_event(struct notifier_block *this, unsigned long event,
+        void *ptr)
+{
+       if (test_and_set_bit(MACHINE_PANICED, &machine_state))
+               return NOTIFY_DONE;
+
+       /* Switch off green LED */
+       omap_set_gpio_dataout(4, 1);
+       /* Flash red LED */
+       omap_writeb(0x78, OMAP_LPG1_LCR);
+       omap_writeb(0x01, OMAP_LPG1_PMR);       /* Enable clock */
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block panic_block = {
+       .notifier_call  = panic_event,
+};
+
+static int __init netstar_late_init(void)
+{
+       /* TODO: Setup front panel switch here */
+
+       /* Setup panic notifier */
+       notifier_chain_register(&panic_notifier_list, &panic_block);
+
+       return 0;
+}
+
+postcore_initcall(netstar_late_init);
+
+MACHINE_START(NETSTAR, "NetStar OMAP5910")
+       MAINTAINER("Ladislav Michl <michl@2n.cz>")
+       BOOT_MEM(0x10000000, 0xfff00000, 0xfef00000)
+       BOOT_PARAMS(0x10000100)
+       MAPIO(netstar_map_io)
+       INITIRQ(netstar_init_irq)
+       INIT_MACHINE(netstar_init)
+       .timer = &omap_timer,
+MACHINE_END
diff --git a/arch/arm/mach-omap/board-voiceblue.c b/arch/arm/mach-omap/board-voiceblue.c
new file mode 100644 (file)
index 0000000..f1a5bff
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * linux/arch/arm/mach-omap/board-voiceblue.c
+ *
+ * Modified from board-generic.c
+ *
+ * Copyright (C) 2004 2N Telekomunikace, Ladislav Michl <michl@2n.cz>
+ *
+ * Code for OMAP5910 based VoiceBlue board (VoIP to GSM gateway).
+ *
+ * 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/device.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_reg.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <asm/arch/gpio.h>
+#include <asm/arch/tc.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/usb.h>
+
+#include "common.h"
+
+extern void omap_init_time(void);
+extern int omap_gpio_init(void);
+
+static struct plat_serial8250_port voiceblue_ports[] = {
+       {
+               .mapbase        = (unsigned long)(OMAP_CS1_PHYS + 0x40000),
+               .irq            = OMAP_GPIO_IRQ(12),
+               .flags          = UPF_BOOT_AUTOCONF | UPF_IOREMAP,
+               .iotype         = UPIO_MEM,
+               .regshift       = 1,
+               .uartclk        = 3686400,
+       },
+       {
+               .mapbase        = (unsigned long)(OMAP_CS1_PHYS + 0x50000),
+               .irq            = OMAP_GPIO_IRQ(13),
+               .flags          = UPF_BOOT_AUTOCONF | UPF_IOREMAP,
+               .iotype         = UPIO_MEM,
+               .regshift       = 1,
+               .uartclk        = 3686400,
+       },
+       {
+               .mapbase        = (unsigned long)(OMAP_CS1_PHYS + 0x60000),
+               .irq            = OMAP_GPIO_IRQ(14),
+               .flags          = UPF_BOOT_AUTOCONF | UPF_IOREMAP,
+               .iotype         = UPIO_MEM,
+               .regshift       = 1,
+               .uartclk        = 3686400,
+       },
+       {
+               .mapbase        = (unsigned long)(OMAP_CS1_PHYS + 0x70000),
+               .irq            = OMAP_GPIO_IRQ(15),
+               .flags          = UPF_BOOT_AUTOCONF | UPF_IOREMAP,
+               .iotype         = UPIO_MEM,
+               .regshift       = 1,
+               .uartclk        = 3686400,
+       },
+       { },
+};
+
+static struct platform_device serial_device = {
+       .name                   = "serial8250",
+       .id                     = 1,
+       .dev                    = {
+               .platform_data  = voiceblue_ports,
+       },
+};
+
+static int __init ext_uart_init(void)
+{
+       return platform_device_register(&serial_device);
+}
+arch_initcall(ext_uart_init);
+
+static struct resource voiceblue_smc91x_resources[] = {
+       [0] = {
+               .start  = OMAP_CS2_PHYS + 0x300,
+               .end    = OMAP_CS2_PHYS + 0x300 + 16,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = OMAP_GPIO_IRQ(8),
+               .end    = OMAP_GPIO_IRQ(8),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device voiceblue_smc91x_device = {
+       .name           = "smc91x",
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(voiceblue_smc91x_resources),
+       .resource       = voiceblue_smc91x_resources,
+};
+
+static struct platform_device *voiceblue_devices[] __initdata = {
+       &voiceblue_smc91x_device,
+};
+
+static struct omap_usb_config voiceblue_usb_config __initdata = {
+       .hmc_mode       = 3,
+       .register_host  = 1,
+       .register_dev   = 1,
+       .pins[0]        = 2,
+       .pins[1]        = 6,
+       .pins[2]        = 6,
+};
+
+static struct omap_board_config_kernel voiceblue_config[] = {
+       { OMAP_TAG_USB, &voiceblue_usb_config },
+};
+
+static void __init voiceblue_init_irq(void)
+{
+       omap_init_irq();
+       omap_gpio_init();
+}
+
+static void __init voiceblue_init(void)
+{
+       /* There is a good chance board is going up, so enable Power LED
+        * (it is connected through invertor) */
+       omap_writeb(0x00, OMAP_LPG1_LCR);
+       /* Watchdog */
+       omap_request_gpio(0);
+       /* smc91x reset */
+       omap_request_gpio(7);
+       omap_set_gpio_direction(7, 0);
+       omap_set_gpio_dataout(7, 1);
+       udelay(2);      /* wait at least 100ns */
+       omap_set_gpio_dataout(7, 0);
+       mdelay(50);     /* 50ms until PHY ready */
+       /* smc91x interrupt pin */
+       omap_request_gpio(8);
+       omap_set_gpio_edge_ctrl(8, OMAP_GPIO_RISING_EDGE);
+       /* 16C554 reset*/
+       omap_request_gpio(6);
+       omap_set_gpio_direction(6, 0);
+       omap_set_gpio_dataout(6, 0);
+       /* 16C554 interrupt pins */
+       omap_request_gpio(12);
+       omap_request_gpio(13);
+       omap_request_gpio(14);
+       omap_request_gpio(15);
+       omap_set_gpio_edge_ctrl(12, OMAP_GPIO_RISING_EDGE);
+       omap_set_gpio_edge_ctrl(13, OMAP_GPIO_RISING_EDGE);
+       omap_set_gpio_edge_ctrl(14, OMAP_GPIO_RISING_EDGE);
+       omap_set_gpio_edge_ctrl(15, OMAP_GPIO_RISING_EDGE);
+
+       platform_add_devices(voiceblue_devices, ARRAY_SIZE(voiceblue_devices));
+       omap_board_config = voiceblue_config;
+       omap_board_config_size = ARRAY_SIZE(voiceblue_config);
+}
+
+static int __initdata omap_serial_ports[OMAP_MAX_NR_PORTS] = {1, 1, 1};
+
+static void __init voiceblue_map_io(void)
+{
+       omap_map_io();
+       omap_serial_init(omap_serial_ports);
+}
+
+#define MACHINE_PANICED                1
+#define MACHINE_REBOOTING      2
+#define MACHINE_REBOOT         4
+static unsigned long machine_state;
+
+static int panic_event(struct notifier_block *this, unsigned long event,
+        void *ptr)
+{
+       if (test_and_set_bit(MACHINE_PANICED, &machine_state))
+               return NOTIFY_DONE;
+
+       /* Flash Power LED
+        * (TODO: Enable clock right way (enabled in bootloader already)) */
+       omap_writeb(0x78, OMAP_LPG1_LCR);
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block panic_block = {
+       .notifier_call  = panic_event,
+};
+
+static int __init setup_notifier(void)
+{
+       /* Setup panic notifier */
+       notifier_chain_register(&panic_notifier_list, &panic_block);
+
+       return 0;
+}
+
+postcore_initcall(setup_notifier);
+
+static int wdt_gpio_state;
+
+void voiceblue_wdt_enable(void)
+{
+       omap_set_gpio_direction(0, 0);
+       omap_set_gpio_dataout(0, 0);
+       omap_set_gpio_dataout(0, 1);
+       omap_set_gpio_dataout(0, 0);
+       wdt_gpio_state = 0;
+}
+
+void voiceblue_wdt_disable(void)
+{
+       omap_set_gpio_dataout(0, 0);
+       omap_set_gpio_dataout(0, 1);
+       omap_set_gpio_dataout(0, 0);
+       omap_set_gpio_direction(0, 1);
+}
+
+void voiceblue_wdt_ping(void)
+{
+       if (test_bit(MACHINE_REBOOT, &machine_state))
+               return;
+
+       wdt_gpio_state = !wdt_gpio_state;
+       omap_set_gpio_dataout(0, wdt_gpio_state);
+}
+
+void voiceblue_reset(void)
+{
+       set_bit(MACHINE_REBOOT, &machine_state);
+       voiceblue_wdt_enable();
+       while (1) ;
+}
+
+EXPORT_SYMBOL(voiceblue_wdt_enable);
+EXPORT_SYMBOL(voiceblue_wdt_disable);
+EXPORT_SYMBOL(voiceblue_wdt_ping);
+
+MACHINE_START(VOICEBLUE, "VoiceBlue OMAP5910")
+       MAINTAINER("Ladislav Michl <michl@2n.cz>")
+       BOOT_MEM(0x10000000, 0xfff00000, 0xfef00000)
+       BOOT_PARAMS(0x10000100)
+       MAPIO(voiceblue_map_io)
+       INITIRQ(voiceblue_init_irq)
+       INIT_MACHINE(voiceblue_init)
+       .timer = &omap_timer,
+MACHINE_END
diff --git a/arch/arm/mach-omap/leds-osk.c b/arch/arm/mach-omap/leds-osk.c
new file mode 100644 (file)
index 0000000..f5177f4
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * linux/arch/arm/mach-omap/leds-osk.c
+ *
+ * LED driver for OSK, and optionally Mistral QVGA, boards
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+
+#include <asm/hardware.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+
+#include <asm/arch/gpio.h>
+#include <asm/arch/tps65010.h>
+
+#include "leds.h"
+
+
+#define LED_STATE_ENABLED      (1 << 0)
+#define LED_STATE_CLAIMED      (1 << 1)
+static u8 led_state;
+
+#define        GREEN_LED               (1 << 0)        /* TPS65010 LED1 */
+#define        AMBER_LED               (1 << 1)        /* TPS65010 LED2 */
+#define        RED_LED                 (1 << 2)        /* TPS65010 GPIO2 */
+#define        TIMER_LED               (1 << 3)        /* Mistral board */
+#define        IDLE_LED                (1 << 4)        /* Mistral board */
+static u8 hw_led_state;
+
+
+/* TPS65010 leds are changed using i2c -- from a task context.
+ * Using one of these for the "idle" LED would be impractical...
+ */
+#define        TPS_LEDS        (GREEN_LED | RED_LED | AMBER_LED)
+
+static u8 tps_leds_change;
+
+static void tps_work(void *unused)
+{
+       for (;;) {
+               u8      leds;
+
+               local_irq_disable();
+               leds = tps_leds_change;
+               tps_leds_change = 0;
+               local_irq_enable();
+
+               if (!leds)
+                       break;
+
+               /* careful:  the set_led() value is on/off/blink */
+               if (leds & GREEN_LED)
+                       tps65010_set_led(LED1, !!(hw_led_state & GREEN_LED));
+               if (leds & AMBER_LED)
+                       tps65010_set_led(LED2, !!(hw_led_state & AMBER_LED));
+
+               /* the gpio led doesn't have that issue */
+               if (leds & RED_LED)
+                       tps65010_set_gpio_out_value(GPIO2,
+                                       !(hw_led_state & RED_LED));
+       }
+}
+
+static DECLARE_WORK(work, tps_work, NULL);
+
+#ifdef CONFIG_FB_OMAP
+
+/* For now, all system indicators require the Mistral board, since that
+ * LED can be manipulated without a task context.  This LED is either red,
+ * or green, but not both; it can't give the full "disco led" effect.
+ */
+
+#define GPIO_LED_RED           3
+#define GPIO_LED_GREEN         OMAP_MPUIO(4)
+
+static void mistral_setled(void)
+{
+       int     red = 0;
+       int     green = 0;
+
+       if (hw_led_state & TIMER_LED)
+               red = 1;
+       else if (hw_led_state & IDLE_LED)
+               green = 1;
+       // else both sides are disabled
+
+       omap_set_gpio_dataout(GPIO_LED_GREEN, green);
+       omap_set_gpio_dataout(GPIO_LED_RED, red);
+}
+
+#endif
+
+void osk_leds_event(led_event_t evt)
+{
+       unsigned long   flags;
+       u16             leds;
+
+       local_irq_save(flags);
+
+       if (!(led_state & LED_STATE_ENABLED) && evt != led_start)
+               goto done;
+
+       leds = hw_led_state;
+       switch (evt) {
+       case led_start:
+               led_state |= LED_STATE_ENABLED;
+               hw_led_state = 0;
+               leds = ~0;
+               break;
+
+       case led_halted:
+       case led_stop:
+               led_state &= ~LED_STATE_ENABLED;
+               hw_led_state = 0;
+               // NOTE:  work may still be pending!!
+               break;
+
+       case led_claim:
+               led_state |= LED_STATE_CLAIMED;
+               hw_led_state = 0;
+               leds = ~0;
+               break;
+
+       case led_release:
+               led_state &= ~LED_STATE_CLAIMED;
+               hw_led_state = 0;
+               break;
+
+#ifdef CONFIG_FB_OMAP
+
+#ifdef CONFIG_LEDS_TIMER
+       case led_timer:
+               hw_led_state ^= TIMER_LED;
+               mistral_setled();
+               break;
+#endif
+
+#ifdef CONFIG_LEDS_CPU
+       case led_idle_start:
+               hw_led_state |= IDLE_LED;
+               mistral_setled();
+               break;
+
+       case led_idle_end:
+               hw_led_state &= ~IDLE_LED;
+               mistral_setled();
+               break;
+#endif
+
+#endif /* CONFIG_FB_OMAP */
+
+       /* "green" == tps LED1 (leftmost, normally power-good)
+        * works only with DC adapter, not on battery power!
+        */
+       case led_green_on:
+               if (led_state & LED_STATE_CLAIMED)
+                       hw_led_state |= GREEN_LED;
+               break;
+       case led_green_off:
+               if (led_state & LED_STATE_CLAIMED)
+                       hw_led_state &= ~GREEN_LED;
+               break;
+
+       /* "amber" == tps LED2 (middle) */
+       case led_amber_on:
+               if (led_state & LED_STATE_CLAIMED)
+                       hw_led_state |= AMBER_LED;
+               break;
+       case led_amber_off:
+               if (led_state & LED_STATE_CLAIMED)
+                       hw_led_state &= ~AMBER_LED;
+               break;
+
+       /* "red" == LED on tps gpio3 (rightmost) */
+       case led_red_on:
+               if (led_state & LED_STATE_CLAIMED)
+                       hw_led_state |= RED_LED;
+               break;
+       case led_red_off:
+               if (led_state & LED_STATE_CLAIMED)
+                       hw_led_state &= ~RED_LED;
+               break;
+
+       default:
+               break;
+       }
+
+       leds ^= hw_led_state;
+       leds &= TPS_LEDS;
+       if (leds && (led_state & LED_STATE_CLAIMED)) {
+               tps_leds_change |= leds;
+               schedule_work(&work);
+       }
+
+done:
+       local_irq_restore(flags);
+}
diff --git a/arch/arm/mach-pxa/poodle.c b/arch/arm/mach-pxa/poodle.c
new file mode 100644 (file)
index 0000000..b6c746e
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * linux/arch/arm/mach-pxa/poodle.c
+ *
+ *  Support for the SHARP Poodle Board.
+ *
+ * Based on:
+ *  linux/arch/arm/mach-pxa/lubbock.c Author:  Nicolas Pitre
+ *
+ *  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.
+ *
+ * Change Log
+ *  12-Dec-2002 Sharp Corporation for Poodle
+ *  John Lenz <lenz@cs.wisc.edu> updates to 2.6
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fb.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+#include <asm/setup.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/poodle.h>
+#include <asm/arch/pxafb.h>
+
+#include <asm/hardware/scoop.h>
+#include <asm/hardware/locomo.h>
+#include <asm/mach/sharpsl_param.h>
+
+#include "generic.h"
+
+static struct resource poodle_scoop_resources[] = {
+       [0] = {
+               .start          = 0x10800000,
+               .end            = 0x10800fff,
+               .flags          = IORESOURCE_MEM,
+       },
+};
+
+static struct scoop_config poodle_scoop_setup = {
+       .io_dir         = POODLE_SCOOP_IO_DIR,
+       .io_out         = POODLE_SCOOP_IO_OUT,
+};
+
+struct platform_device poodle_scoop_device = {
+       .name           = "sharp-scoop",
+       .id             = -1,
+       .dev            = {
+               .platform_data  = &poodle_scoop_setup,
+       },
+       .num_resources  = ARRAY_SIZE(poodle_scoop_resources),
+       .resource       = poodle_scoop_resources,
+};
+
+
+/* LoCoMo device */
+static struct resource locomo_resources[] = {
+       [0] = {
+               .start          = 0x10000000,
+               .end            = 0x10001fff,
+               .flags          = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start          = IRQ_GPIO(10),
+               .end            = IRQ_GPIO(10),
+               .flags          = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device locomo_device = {
+       .name           = "locomo",
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(locomo_resources),
+       .resource       = locomo_resources,
+};
+
+/* PXAFB device */
+static struct pxafb_mach_info poodle_fb_info __initdata = {
+       .pixclock       = 144700,
+
+       .xres           = 320,
+       .yres           = 240,
+       .bpp            = 16,
+
+       .hsync_len      = 7,
+       .left_margin    = 11,
+       .right_margin   = 30,
+
+       .vsync_len      = 2,
+       .upper_margin   = 2,
+       .lower_margin   = 0,
+       .sync           = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+
+       .lccr0          = LCCR0_Act | LCCR0_Sngl | LCCR0_Color,
+       .lccr3          = 0,
+
+       .pxafb_backlight_power  = NULL,
+       .pxafb_lcd_power        = NULL,
+};
+
+static struct platform_device *devices[] __initdata = {
+       &locomo_device,
+       &poodle_scoop_device,
+};
+
+static void __init poodle_init(void)
+{
+       int ret = 0;
+
+       /* cpu initialize */
+       /* Pgsr Register */
+       PGSR0 = 0x0146dd80;
+       PGSR1 = 0x03bf0890;
+       PGSR2 = 0x0001c000;
+
+       /* Alternate Register */
+       GAFR0_L = 0x01001000;
+       GAFR0_U = 0x591a8010;
+       GAFR1_L = 0x900a8451;
+       GAFR1_U = 0xaaa5aaaa;
+       GAFR2_L = 0x8aaaaaaa;
+       GAFR2_U = 0x00000002;
+
+       /* Direction Register */
+       GPDR0 = 0xd3f0904c;
+       GPDR1 = 0xfcffb7d3;
+       GPDR2 = 0x0001ffff;
+
+       /* Output Register */
+       GPCR0 = 0x00000000;
+       GPCR1 = 0x00000000;
+       GPCR2 = 0x00000000;
+
+       GPSR0 = 0x00400000;
+       GPSR1 = 0x00000000;
+        GPSR2 = 0x00000000;
+
+       set_pxa_fb_info(&poodle_fb_info);
+
+       ret = platform_add_devices(devices, ARRAY_SIZE(devices));
+       if (ret) {
+               printk(KERN_WARNING "poodle: Unable to register LoCoMo device\n");
+       }
+}
+
+static void __init fixup_poodle(struct machine_desc *desc,
+               struct tag *tags, char **cmdline, struct meminfo *mi)
+{
+       sharpsl_save_param();
+}
+
+static struct map_desc poodle_io_desc[] __initdata = {
+ /* virtual     physical    length                   */
+  { 0xef800000, 0x00000000, 0x00800000, MT_DEVICE }, /* Boot Flash */
+};
+
+static void __init poodle_map_io(void)
+{
+       pxa_map_io();
+       iotable_init(poodle_io_desc, ARRAY_SIZE(poodle_io_desc));
+
+       /* setup sleep mode values */
+       PWER  = 0x00000002;
+       PFER  = 0x00000000;
+       PRER  = 0x00000002;
+       PGSR0 = 0x00008000;
+       PGSR1 = 0x003F0202;
+       PGSR2 = 0x0001C000;
+       PCFR |= PCFR_OPDE;
+}
+
+MACHINE_START(POODLE, "SHARP Poodle")
+       BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000))
+       FIXUP(fixup_poodle)
+       MAPIO(poodle_map_io)
+       INITIRQ(pxa_init_irq)
+       .timer = &pxa_timer,
+       .init_machine = poodle_init,
+MACHINE_END
diff --git a/arch/arm/mach-s3c2410/mach-n30.c b/arch/arm/mach-s3c2410/mach-n30.c
new file mode 100644 (file)
index 0000000..bd15998
--- /dev/null
@@ -0,0 +1,155 @@
+/* linux/arch/arm/mach-s3c2410/mach-n30.c
+ *
+ * Copyright (c) 2003-2005 Simtec Electronics
+ *     Ben Dooks <ben@simtec.co.uk>
+ *
+ * Copyright (c) 2005 Christer Weinigel <christer@weinigel.se>
+ *
+ * There is a wiki with more information about the n30 port at
+ * http://handhelds.org/moin/moin.cgi/AcerN30Documentation .
+ *
+ * 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/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kthread.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 <asm/arch/iic.h>
+
+#include <linux/serial_core.h>
+
+#include "s3c2410.h"
+#include "clock.h"
+#include "devs.h"
+#include "cpu.h"
+
+static struct map_desc n30_iodesc[] __initdata = {
+       /* nothing here yet */
+};
+
+static struct s3c2410_uartcfg n30_uartcfgs[] = {
+       /* Normal serial port */
+       [0] = {
+               .hwport      = 0,
+               .flags       = 0,
+               .ucon        = 0x2c5,
+               .ulcon       = 0x03,
+               .ufcon       = 0x51,
+       },
+       /* IR port */
+       [1] = {
+               .hwport      = 1,
+               .flags       = 0,
+               .uart_flags  = UPF_CONS_FLOW,
+               .ucon        = 0x2c5,
+               .ulcon       = 0x43,
+               .ufcon       = 0x51,
+       },
+       /* The BlueTooth controller is connected to port 2 */
+       [2] = {
+               .hwport      = 2,
+               .flags       = 0,
+               .ucon        = 0x2c5,
+               .ulcon       = 0x03,
+               .ufcon       = 0x51,
+       },
+};
+
+static struct platform_device *n30_devices[] __initdata = {
+       &s3c_device_usb,
+       &s3c_device_lcd,
+       &s3c_device_wdt,
+       &s3c_device_i2c,
+       &s3c_device_iis,
+       &s3c_device_usbgadget,
+};
+
+static struct s3c2410_platform_i2c n30_i2ccfg = {
+       .flags          = 0,
+       .slave_addr     = 0x10,
+       .bus_freq       = 10*1000,
+       .max_freq       = 10*1000,
+};
+
+static struct s3c24xx_board n30_board __initdata = {
+       .devices       = n30_devices,
+       .devices_count = ARRAY_SIZE(n30_devices)
+};
+
+void __init n30_map_io(void)
+{
+       s3c24xx_init_io(n30_iodesc, ARRAY_SIZE(n30_iodesc));
+       s3c24xx_init_clocks(0);
+       s3c24xx_init_uarts(n30_uartcfgs, ARRAY_SIZE(n30_uartcfgs));
+       s3c24xx_set_board(&n30_board);
+}
+
+void __init n30_init_irq(void)
+{
+       s3c24xx_init_irq();
+}
+
+
+static int n30_usbstart_thread(void *unused)
+{
+       /* Turn off suspend on both USB ports, and switch the
+        * selectable USB port to USB device mode. */
+       writel(readl(S3C2410_MISCCR) & ~0x00003008, S3C2410_MISCCR);
+
+       /* Turn off the D+ pull up for 3 seconds so that the USB host
+        * at the other end will do a rescan of the USB bus.  */
+       s3c2410_gpio_setpin(S3C2410_GPB3, 0);
+
+       msleep_interruptible(3*HZ);
+
+       s3c2410_gpio_setpin(S3C2410_GPB3, 1);
+
+       return 0;
+}
+
+
+void __init n30_init(void)
+{
+       s3c_device_i2c.dev.platform_data = &n30_i2ccfg;
+
+       kthread_run(n30_usbstart_thread, NULL, "n30_usbstart");
+}
+
+MACHINE_START(N30, "Acer-N30")
+     MAINTAINER("Christer Weinigel <christer@weinigel.se>, Ben Dooks <ben-linux@fluff.org>")
+     BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART)
+     BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100)
+
+       .timer          = &s3c24xx_timer,
+       .init_machine   = n30_init,
+       .init_irq       = n30_init_irq,
+       .map_io         = n30_map_io,
+MACHINE_END
+
+/*
+    Local variables:
+        compile-command: "make ARCH=arm CROSS_COMPILE=/usr/local/arm/3.3.2/bin/arm-linux- -k -C ../../.."
+        c-basic-offset: 8
+    End:
+*/
diff --git a/arch/arm/mach-s3c2410/mach-nexcoder.c b/arch/arm/mach-s3c2410/mach-nexcoder.c
new file mode 100644 (file)
index 0000000..70487bf
--- /dev/null
@@ -0,0 +1,156 @@
+/* linux/arch/arm/mach-s3c2410/mach-nexcoder.c
+ *
+ * Copyright (c) 2004 Nex Vision
+ *   Guillaume GOURAT <guillaume.gourat@nexvision.tv>
+ *
+ * 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:
+ *     15-10-2004 GG  Created initial version
+ *     12-03-2005 BJD Updated for release
+ */
+
+#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/string.h>
+#include <linux/device.h>
+
+#include <linux/mtd/map.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <asm/setup.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+//#include <asm/debug-ll.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-serial.h>
+
+#include "s3c2410.h"
+#include "s3c2440.h"
+#include "clock.h"
+#include "devs.h"
+#include "cpu.h"
+
+static struct map_desc nexcoder_iodesc[] __initdata = {
+       /* nothing here yet */
+};
+
+#define UCON S3C2410_UCON_DEFAULT
+#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
+#define UFCON S3C2410_UFCON_RXTRIG12 | S3C2410_UFCON_FIFOMODE
+
+static struct s3c2410_uartcfg nexcoder_uartcfgs[] = {
+       [0] = {
+               .hwport      = 0,
+               .flags       = 0,
+               .ucon        = UCON,
+               .ulcon       = ULCON,
+               .ufcon       = UFCON,
+       },
+       [1] = {
+               .hwport      = 1,
+               .flags       = 0,
+               .ucon        = UCON,
+               .ulcon       = ULCON,
+               .ufcon       = UFCON,
+       },
+       [2] = {
+               .hwport      = 2,
+               .flags       = 0,
+               .ucon        = UCON,
+               .ulcon       = ULCON,
+               .ufcon       = UFCON,
+       }
+};
+
+/* NOR Flash on NexVision NexCoder 2440 board */
+
+static struct resource nexcoder_nor_resource[] = {
+       [0] = {
+               .start = S3C2410_CS0,
+               .end   = S3C2410_CS0 + (8*1024*1024) - 1,
+               .flags = IORESOURCE_MEM,
+       }
+};
+
+static struct map_info nexcoder_nor_map = {
+       .bankwidth = 2,
+};
+
+static struct platform_device nexcoder_device_nor = {
+       .name           = "mtd-flash",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(nexcoder_nor_resource),
+       .resource       = nexcoder_nor_resource,
+       .dev =
+       {
+               .platform_data = &nexcoder_nor_map,
+       }
+};
+
+/* Standard Nexcoder devices */
+
+static struct platform_device *nexcoder_devices[] __initdata = {
+       &s3c_device_usb,
+       &s3c_device_lcd,
+       &s3c_device_wdt,
+       &s3c_device_i2c,
+       &s3c_device_iis,
+       &s3c_device_rtc,
+       &s3c_device_camif,
+       &s3c_device_spi0,
+       &s3c_device_spi1,
+       &nexcoder_device_nor,
+};
+
+static struct s3c24xx_board nexcoder_board __initdata = {
+       .devices       = nexcoder_devices,
+       .devices_count = ARRAY_SIZE(nexcoder_devices),
+};
+
+
+static void __init nexcoder_sensorboard_init(void)
+{
+       // Initialize SCCB bus
+       s3c2410_gpio_setpin(S3C2410_GPE14, 1); // IICSCL
+       s3c2410_gpio_cfgpin(S3C2410_GPE14, S3C2410_GPE14_OUTP);
+       s3c2410_gpio_setpin(S3C2410_GPE15, 1); // IICSDA
+       s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_OUTP);
+
+       // Power up the sensor board
+       s3c2410_gpio_setpin(S3C2410_GPF1, 1);
+       s3c2410_gpio_cfgpin(S3C2410_GPF1, S3C2410_GPF1_OUTP); // CAM_GPIO7 => nLDO_PWRDN
+       s3c2410_gpio_setpin(S3C2410_GPF2, 0);
+       s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_OUTP); // CAM_GPIO6 => CAM_PWRDN
+}
+
+void __init nexcoder_map_io(void)
+{
+       s3c24xx_init_io(nexcoder_iodesc, ARRAY_SIZE(nexcoder_iodesc));
+       s3c24xx_init_clocks(0);
+       s3c24xx_init_uarts(nexcoder_uartcfgs, ARRAY_SIZE(nexcoder_uartcfgs));
+       s3c24xx_set_board(&nexcoder_board);
+       nexcoder_sensorboard_init();
+}
+
+
+MACHINE_START(NEXCODER_2440, "NexVision - Nexcoder 2440")
+     MAINTAINER("Guillaume GOURAT <guillaume.gourat@nexvision.tv>")
+     BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART)
+     BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100)
+       .map_io         = nexcoder_map_io,
+       .init_irq       = s3c24xx_init_irq,
+       .timer          = &s3c24xx_timer,
+MACHINE_END
diff --git a/arch/arm/mach-s3c2410/mach-otom.c b/arch/arm/mach-s3c2410/mach-otom.c
new file mode 100644 (file)
index 0000000..67d8ce8
--- /dev/null
@@ -0,0 +1,124 @@
+/* linux/arch/arm/mach-s3c2410/mach-otom.c
+ *
+ * Copyright (c) 2004 Nex Vision
+ *   Guillaume GOURAT <guillaume.gourat@nexvision.fr>
+ *
+ * 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/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/device.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <asm/arch/otom-map.h>
+
+#include <asm/hardware.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 "clock.h"
+#include "devs.h"
+#include "cpu.h"
+
+static struct map_desc otom11_iodesc[] __initdata = {
+  /* Device area */
+       { (u32)OTOM_VA_CS8900A_BASE, OTOM_PA_CS8900A_BASE, SZ_16M, MT_DEVICE },
+};
+
+#define UCON S3C2410_UCON_DEFAULT
+#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
+#define UFCON S3C2410_UFCON_RXTRIG12 | S3C2410_UFCON_FIFOMODE
+
+static struct s3c2410_uartcfg otom11_uartcfgs[] = {
+       [0] = {
+               .hwport      = 0,
+               .flags       = 0,
+               .ucon        = UCON,
+               .ulcon       = ULCON,
+               .ufcon       = UFCON,
+       },
+       [1] = {
+               .hwport      = 1,
+               .flags       = 0,
+               .ucon        = UCON,
+               .ulcon       = ULCON,
+               .ufcon       = UFCON,
+       },
+       /* port 2 is not actually used */
+       [2] = {
+               .hwport      = 2,
+               .flags       = 0,
+               .ucon        = UCON,
+               .ulcon       = ULCON,
+               .ufcon       = UFCON,
+       }
+};
+
+/* NOR Flash on NexVision OTOM board */
+
+static struct resource otom_nor_resource[] = {
+       [0] = {
+               .start = S3C2410_CS0,
+               .end   = S3C2410_CS0 + (4*1024*1024) - 1,
+               .flags = IORESOURCE_MEM,
+       }
+};
+
+static struct platform_device otom_device_nor = {
+       .name           = "mtd-flash",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(otom_nor_resource),
+       .resource       = otom_nor_resource,
+};
+
+/* Standard OTOM devices */
+
+static struct platform_device *otom11_devices[] __initdata = {
+       &s3c_device_usb,
+       &s3c_device_lcd,
+       &s3c_device_wdt,
+       &s3c_device_i2c,
+       &s3c_device_iis,
+       &s3c_device_rtc,
+       &otom_device_nor,
+};
+
+static struct s3c24xx_board otom11_board __initdata = {
+       .devices       = otom11_devices,
+       .devices_count = ARRAY_SIZE(otom11_devices)
+};
+
+
+void __init otom11_map_io(void)
+{
+       s3c24xx_init_io(otom11_iodesc, ARRAY_SIZE(otom11_iodesc));
+       s3c24xx_init_clocks(0);
+       s3c24xx_init_uarts(otom11_uartcfgs, ARRAY_SIZE(otom11_uartcfgs));
+       s3c24xx_set_board(&otom11_board);
+}
+
+
+MACHINE_START(OTOM, "Nex Vision - Otom 1.1")
+     MAINTAINER("Guillaume GOURAT <guillaume.gourat@nexvision.tv>")
+     BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART)
+     BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100)
+       .map_io         = otom11_map_io,
+       .init_irq       = s3c24xx_init_irq,
+       .timer          = &s3c24xx_timer,
+MACHINE_END
diff --git a/arch/arm/mach-s3c2410/mach-smdk2440.c b/arch/arm/mach-s3c2410/mach-smdk2440.c
new file mode 100644 (file)
index 0000000..7857176
--- /dev/null
@@ -0,0 +1,135 @@
+/* linux/arch/arm/mach-s3c2410/mach-smdk2440.c
+ *
+ * Copyright (c) 2004,2005 Simtec Electronics
+ *     Ben Dooks <ben@simtec.co.uk>
+ *
+ * http://www.fluff.org/ben/smdk2440/
+ *
+ * Thanks to Dimity Andric and TomTom for the loan of an SMDK2440.
+ *
+ * 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:
+ *     01-Nov-2004 BJD   Initial version
+ *     12-Nov-2004 BJD   Updated for release
+ *     04-Jan-2005 BJD   Fixes for pre-release
+ *     22-Feb-2005 BJD   Updated for 2.6.11-rc5 relesa
+ *     10-Mar-2005 LCVR  Replaced S3C2410_VA by S3C24XX_VA
+ *     14-Mar-2005 BJD   void __iomem fixes
+*/
+
+#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 <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/debug-ll.h>
+#include <asm/arch/regs-serial.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/idle.h>
+
+#include "s3c2410.h"
+#include "s3c2440.h"
+#include "clock.h"
+#include "devs.h"
+#include "cpu.h"
+#include "pm.h"
+
+static struct map_desc smdk2440_iodesc[] __initdata = {
+       /* ISA IO Space map (memory space selected by A24) */
+
+       { (u32)S3C24XX_VA_ISA_WORD, S3C2410_CS2, SZ_16M, MT_DEVICE },
+       { (u32)S3C24XX_VA_ISA_BYTE, S3C2410_CS2, SZ_16M, MT_DEVICE },
+};
+
+#define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK
+#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
+#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE
+
+static struct s3c2410_uartcfg smdk2440_uartcfgs[] = {
+       [0] = {
+               .hwport      = 0,
+               .flags       = 0,
+               .ucon        = 0x3c5,
+               .ulcon       = 0x03,
+               .ufcon       = 0x51,
+       },
+       [1] = {
+               .hwport      = 1,
+               .flags       = 0,
+               .ucon        = 0x3c5,
+               .ulcon       = 0x03,
+               .ufcon       = 0x51,
+       },
+       /* IR port */
+       [2] = {
+               .hwport      = 2,
+               .flags       = 0,
+               .ucon        = 0x3c5,
+               .ulcon       = 0x43,
+               .ufcon       = 0x51,
+       }
+};
+
+static struct platform_device *smdk2440_devices[] __initdata = {
+       &s3c_device_usb,
+       &s3c_device_lcd,
+       &s3c_device_wdt,
+       &s3c_device_i2c,
+       &s3c_device_iis,
+};
+
+static struct s3c24xx_board smdk2440_board __initdata = {
+       .devices       = smdk2440_devices,
+       .devices_count = ARRAY_SIZE(smdk2440_devices)
+};
+
+void __init smdk2440_map_io(void)
+{
+       s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
+       s3c24xx_init_clocks(16934400);
+       s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
+       s3c24xx_set_board(&smdk2440_board);
+}
+
+void __init smdk2440_machine_init(void)
+{
+       /* Configure the LEDs (even if we have no LED support)*/
+
+       s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);
+       s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
+       s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP);
+       s3c2410_gpio_cfgpin(S3C2410_GPF7, S3C2410_GPF7_OUTP);
+
+       s3c2410_gpio_setpin(S3C2410_GPF4, 0);
+       s3c2410_gpio_setpin(S3C2410_GPF5, 0);
+       s3c2410_gpio_setpin(S3C2410_GPF6, 0);
+       s3c2410_gpio_setpin(S3C2410_GPF7, 0);
+
+       s3c2410_pm_init();
+}
+
+MACHINE_START(S3C2440, "SMDK2440")
+       MAINTAINER("Ben Dooks <ben@fluff.org>")
+       BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, (u32)S3C24XX_VA_UART)
+       BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100)
+
+       .init_irq       = s3c24xx_init_irq,
+       .map_io         = smdk2440_map_io,
+       .init_machine   = smdk2440_machine_init,
+       .timer          = &s3c24xx_timer,
+MACHINE_END
diff --git a/arch/arm/mm/copypage-v4mc.c b/arch/arm/mm/copypage-v4mc.c
new file mode 100644 (file)
index 0000000..fc69dcc
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ *  linux/arch/arm/lib/copypage-armv4mc.S
+ *
+ *  Copyright (C) 1995-2005 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.
+ *
+ * This handles the mini data cache, as found on SA11x0 and XScale
+ * processors.  When we copy a user page page, we map it in such a way
+ * that accesses to this page will not touch the main data cache, but
+ * will be cached in the mini data cache.  This prevents us thrashing
+ * the main data cache on page faults.
+ */
+#include <linux/init.h>
+#include <linux/mm.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/tlbflush.h>
+
+/*
+ * 0xffff8000 to 0xffffffff is reserved for any ARM architecture
+ * specific hacks for copying pages efficiently.
+ */
+#define minicache_pgprot __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | \
+                                 L_PTE_CACHEABLE)
+
+#define TOP_PTE(x)     pte_offset_kernel(top_pmd, x)
+
+static DEFINE_SPINLOCK(minicache_lock);
+
+/*
+ * ARMv4 mini-dcache optimised copy_user_page
+ *
+ * We flush the destination cache lines just before we write the data into the
+ * corresponding address.  Since the Dcache is read-allocate, this removes the
+ * Dcache aliasing issue.  The writes will be forwarded to the write buffer,
+ * and merged as appropriate.
+ *
+ * Note: We rely on all ARMv4 processors implementing the "invalidate D line"
+ * instruction.  If your processor does not supply this, you have to write your
+ * own copy_user_page that does the right thing.
+ */
+static void __attribute__((naked))
+mc_copy_user_page(void *from, void *to)
+{
+       asm volatile(
+       "stmfd  sp!, {r4, lr}                   @ 2\n\
+       mov     r4, %2                          @ 1\n\
+       ldmia   %0!, {r2, r3, ip, lr}           @ 4\n\
+1:     mcr     p15, 0, %1, c7, c6, 1           @ 1   invalidate D line\n\
+       stmia   %1!, {r2, r3, ip, lr}           @ 4\n\
+       ldmia   %0!, {r2, r3, ip, lr}           @ 4+1\n\
+       stmia   %1!, {r2, r3, ip, lr}           @ 4\n\
+       ldmia   %0!, {r2, r3, ip, lr}           @ 4\n\
+       mcr     p15, 0, %1, c7, c6, 1           @ 1   invalidate D line\n\
+       stmia   %1!, {r2, r3, ip, lr}           @ 4\n\
+       ldmia   %0!, {r2, r3, ip, lr}           @ 4\n\
+       subs    r4, r4, #1                      @ 1\n\
+       stmia   %1!, {r2, r3, ip, lr}           @ 4\n\
+       ldmneia %0!, {r2, r3, ip, lr}           @ 4\n\
+       bne     1b                              @ 1\n\
+       ldmfd   sp!, {r4, pc}                   @ 3"
+       :
+       : "r" (from), "r" (to), "I" (PAGE_SIZE / 64));
+}
+
+void v4_mc_copy_user_page(void *kto, const void *kfrom, unsigned long vaddr)
+{
+       spin_lock(&minicache_lock);
+
+       set_pte(TOP_PTE(0xffff8000), pfn_pte(__pa(kfrom) >> PAGE_SHIFT, minicache_pgprot));
+       flush_tlb_kernel_page(0xffff8000);
+
+       mc_copy_user_page((void *)0xffff8000, kto);
+
+       spin_unlock(&minicache_lock);
+}
+
+/*
+ * ARMv4 optimised clear_user_page
+ */
+void __attribute__((naked))
+v4_mc_clear_user_page(void *kaddr, unsigned long vaddr)
+{
+       asm volatile(
+       "str    lr, [sp, #-4]!\n\
+       mov     r1, %0                          @ 1\n\
+       mov     r2, #0                          @ 1\n\
+       mov     r3, #0                          @ 1\n\
+       mov     ip, #0                          @ 1\n\
+       mov     lr, #0                          @ 1\n\
+1:     mcr     p15, 0, r0, c7, c6, 1           @ 1   invalidate D line\n\
+       stmia   r0!, {r2, r3, ip, lr}           @ 4\n\
+       stmia   r0!, {r2, r3, ip, lr}           @ 4\n\
+       mcr     p15, 0, r0, c7, c6, 1           @ 1   invalidate D line\n\
+       stmia   r0!, {r2, r3, ip, lr}           @ 4\n\
+       stmia   r0!, {r2, r3, ip, lr}           @ 4\n\
+       subs    r1, r1, #1                      @ 1\n\
+       bne     1b                              @ 1\n\
+       ldr     pc, [sp], #4"
+       :
+       : "I" (PAGE_SIZE / 64));
+}
+
+struct cpu_user_fns v4_mc_user_fns __initdata = {
+       .cpu_clear_user_page    = v4_mc_clear_user_page, 
+       .cpu_copy_user_page     = v4_mc_copy_user_page,
+};
diff --git a/arch/arm/mm/copypage-xscale.c b/arch/arm/mm/copypage-xscale.c
new file mode 100644 (file)
index 0000000..42a6ee2
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ *  linux/arch/arm/lib/copypage-xscale.S
+ *
+ *  Copyright (C) 1995-2005 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.
+ *
+ * This handles the mini data cache, as found on SA11x0 and XScale
+ * processors.  When we copy a user page page, we map it in such a way
+ * that accesses to this page will not touch the main data cache, but
+ * will be cached in the mini data cache.  This prevents us thrashing
+ * the main data cache on page faults.
+ */
+#include <linux/init.h>
+#include <linux/mm.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/tlbflush.h>
+
+/*
+ * 0xffff8000 to 0xffffffff is reserved for any ARM architecture
+ * specific hacks for copying pages efficiently.
+ */
+#define COPYPAGE_MINICACHE     0xffff8000
+
+#define minicache_pgprot __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | \
+                                 L_PTE_CACHEABLE)
+
+#define TOP_PTE(x)     pte_offset_kernel(top_pmd, x)
+
+static DEFINE_SPINLOCK(minicache_lock);
+
+/*
+ * XScale mini-dcache optimised copy_user_page
+ *
+ * We flush the destination cache lines just before we write the data into the
+ * corresponding address.  Since the Dcache is read-allocate, this removes the
+ * Dcache aliasing issue.  The writes will be forwarded to the write buffer,
+ * and merged as appropriate.
+ */
+static void __attribute__((naked))
+mc_copy_user_page(void *from, void *to)
+{
+       /*
+        * Strangely enough, best performance is achieved
+        * when prefetching destination as well.  (NP)
+        */
+       asm volatile(
+       "stmfd  sp!, {r4, r5, lr}               \n\
+       mov     lr, %2                          \n\
+       pld     [r0, #0]                        \n\
+       pld     [r0, #32]                       \n\
+       pld     [r1, #0]                        \n\
+       pld     [r1, #32]                       \n\
+1:     pld     [r0, #64]                       \n\
+       pld     [r0, #96]                       \n\
+       pld     [r1, #64]                       \n\
+       pld     [r1, #96]                       \n\
+2:     ldrd    r2, [r0], #8                    \n\
+       ldrd    r4, [r0], #8                    \n\
+       mov     ip, r1                          \n\
+       strd    r2, [r1], #8                    \n\
+       ldrd    r2, [r0], #8                    \n\
+       strd    r4, [r1], #8                    \n\
+       ldrd    r4, [r0], #8                    \n\
+       strd    r2, [r1], #8                    \n\
+       strd    r4, [r1], #8                    \n\
+       mcr     p15, 0, ip, c7, c10, 1          @ clean D line\n\
+       ldrd    r2, [r0], #8                    \n\
+       mcr     p15, 0, ip, c7, c6, 1           @ invalidate D line\n\
+       ldrd    r4, [r0], #8                    \n\
+       mov     ip, r1                          \n\
+       strd    r2, [r1], #8                    \n\
+       ldrd    r2, [r0], #8                    \n\
+       strd    r4, [r1], #8                    \n\
+       ldrd    r4, [r0], #8                    \n\
+       strd    r2, [r1], #8                    \n\
+       strd    r4, [r1], #8                    \n\
+       mcr     p15, 0, ip, c7, c10, 1          @ clean D line\n\
+       subs    lr, lr, #1                      \n\
+       mcr     p15, 0, ip, c7, c6, 1           @ invalidate D line\n\
+       bgt     1b                              \n\
+       beq     2b                              \n\
+       ldmfd   sp!, {r4, r5, pc}               "
+       :
+       : "r" (from), "r" (to), "I" (PAGE_SIZE / 64 - 1));
+}
+
+void xscale_mc_copy_user_page(void *kto, const void *kfrom, unsigned long vaddr)
+{
+       spin_lock(&minicache_lock);
+
+       set_pte(TOP_PTE(COPYPAGE_MINICACHE), pfn_pte(__pa(kfrom) >> PAGE_SHIFT, minicache_pgprot));
+       flush_tlb_kernel_page(COPYPAGE_MINICACHE);
+
+       mc_copy_user_page((void *)COPYPAGE_MINICACHE, kto);
+
+       spin_unlock(&minicache_lock);
+}
+
+/*
+ * XScale optimised clear_user_page
+ */
+void __attribute__((naked))
+xscale_mc_clear_user_page(void *kaddr, unsigned long vaddr)
+{
+       asm volatile(
+       "mov    r1, %0                          \n\
+       mov     r2, #0                          \n\
+       mov     r3, #0                          \n\
+1:     mov     ip, r0                          \n\
+       strd    r2, [r0], #8                    \n\
+       strd    r2, [r0], #8                    \n\
+       strd    r2, [r0], #8                    \n\
+       strd    r2, [r0], #8                    \n\
+       mcr     p15, 0, ip, c7, c10, 1          @ clean D line\n\
+       subs    r1, r1, #1                      \n\
+       mcr     p15, 0, ip, c7, c6, 1           @ invalidate D line\n\
+       bne     1b                              \n\
+       mov     pc, lr"
+       :
+       : "I" (PAGE_SIZE / 32));
+}
+
+struct cpu_user_fns xscale_mc_user_fns __initdata = {
+       .cpu_clear_user_page    = xscale_mc_clear_user_page, 
+       .cpu_copy_user_page     = xscale_mc_copy_user_page,
+};
diff --git a/arch/i386/kernel/cpu/cpufreq/sc520_freq.c b/arch/i386/kernel/cpu/cpufreq/sc520_freq.c
new file mode 100644 (file)
index 0000000..ef457d5
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ *     sc520_freq.c: cpufreq driver for the AMD Elan sc520
+ *
+ *     Copyright (C) 2005 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.
+ *
+ *     Based on elanfreq.c
+ *
+ *     2005-03-30: - initial revision
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/delay.h>
+#include <linux/cpufreq.h>
+
+#include <asm/msr.h>
+#include <asm/timex.h>
+#include <asm/io.h>
+
+#define MMCR_BASE      0xfffef000      /* The default base address */
+#define OFFS_CPUCTL    0x2   /* CPU Control Register */
+
+static __u8 __iomem *cpuctl;
+
+#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "sc520_freq", msg)
+
+static struct cpufreq_frequency_table sc520_freq_table[] = {
+       {0x01,  100000},
+       {0x02,  133000},
+       {0,     CPUFREQ_TABLE_END},
+};
+
+static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu)
+{
+       u8 clockspeed_reg = *cpuctl;
+
+       switch (clockspeed_reg & 0x03) {
+       default:
+               printk(KERN_ERR "sc520_freq: error: cpuctl register has unexpected value %02x\n", clockspeed_reg);
+       case 0x01:
+               return 100000;
+       case 0x02:
+               return 133000;
+       }
+}
+
+static void sc520_freq_set_cpu_state (unsigned int state)
+{
+
+       struct cpufreq_freqs    freqs;
+       u8 clockspeed_reg;
+
+       freqs.old = sc520_freq_get_cpu_frequency(0);
+       freqs.new = sc520_freq_table[state].frequency;
+       freqs.cpu = 0; /* AMD Elan is UP */
+
+       cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+       dprintk("attempting to set frequency to %i kHz\n",
+                       sc520_freq_table[state].frequency);
+
+       local_irq_disable();
+
+       clockspeed_reg = *cpuctl & ~0x03;
+       *cpuctl = clockspeed_reg | sc520_freq_table[state].index;
+
+       local_irq_enable();
+
+       cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+};
+
+static int sc520_freq_verify (struct cpufreq_policy *policy)
+{
+       return cpufreq_frequency_table_verify(policy, &sc520_freq_table[0]);
+}
+
+static int sc520_freq_target (struct cpufreq_policy *policy,
+                           unsigned int target_freq,
+                           unsigned int relation)
+{
+       unsigned int newstate = 0;
+
+       if (cpufreq_frequency_table_target(policy, sc520_freq_table, target_freq, relation, &newstate))
+               return -EINVAL;
+
+       sc520_freq_set_cpu_state(newstate);
+
+       return 0;
+}
+
+
+/*
+ *     Module init and exit code
+ */
+
+static int sc520_freq_cpu_init(struct cpufreq_policy *policy)
+{
+       struct cpuinfo_x86 *c = cpu_data;
+       int result;
+
+       /* capability check */
+       if (c->x86_vendor != X86_VENDOR_AMD ||
+           c->x86 != 4 || c->x86_model != 9)
+               return -ENODEV;
+
+       /* cpuinfo and default policy values */
+       policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+       policy->cpuinfo.transition_latency = 1000000; /* 1ms */
+       policy->cur = sc520_freq_get_cpu_frequency(0);
+
+       result = cpufreq_frequency_table_cpuinfo(policy, sc520_freq_table);
+       if (result)
+               return (result);
+
+       cpufreq_frequency_table_get_attr(sc520_freq_table, policy->cpu);
+
+       return 0;
+}
+
+
+static int sc520_freq_cpu_exit(struct cpufreq_policy *policy)
+{
+       cpufreq_frequency_table_put_attr(policy->cpu);
+       return 0;
+}
+
+
+static struct freq_attr* sc520_freq_attr[] = {
+       &cpufreq_freq_attr_scaling_available_freqs,
+       NULL,
+};
+
+
+static struct cpufreq_driver sc520_freq_driver = {
+       .get    = sc520_freq_get_cpu_frequency,
+       .verify = sc520_freq_verify,
+       .target = sc520_freq_target,
+       .init   = sc520_freq_cpu_init,
+       .exit   = sc520_freq_cpu_exit,
+       .name   = "sc520_freq",
+       .owner  = THIS_MODULE,
+       .attr   = sc520_freq_attr,
+};
+
+
+static int __init sc520_freq_init(void)
+{
+       struct cpuinfo_x86 *c = cpu_data;
+
+       /* Test if we have the right hardware */
+       if(c->x86_vendor != X86_VENDOR_AMD ||
+                               c->x86 != 4 || c->x86_model != 9) {
+               dprintk("no Elan SC520 processor found!\n");
+               return -ENODEV;
+       }
+       cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1);
+       if(!cpuctl) {
+               printk(KERN_ERR "sc520_freq: error: failed to remap memory\n");
+               return -ENOMEM;
+       }
+
+       return cpufreq_register_driver(&sc520_freq_driver);
+}
+
+
+static void __exit sc520_freq_exit(void)
+{
+       cpufreq_unregister_driver(&sc520_freq_driver);
+       iounmap(cpuctl);
+}
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_DESCRIPTION("cpufreq driver for AMD's Elan sc520 CPU");
+
+module_init(sc520_freq_init);
+module_exit(sc520_freq_exit);
+
diff --git a/arch/i386/kernel/reboot_fixups.c b/arch/i386/kernel/reboot_fixups.c
new file mode 100644 (file)
index 0000000..1b183b3
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * linux/arch/i386/kernel/reboot_fixups.c
+ *
+ * This is a good place to put board specific reboot fixups.
+ *
+ * List of supported fixups:
+ * geode-gx1/cs5530a - Jaya Kumar <jayalk@intworks.biz>
+ *
+ */
+
+#include <asm/delay.h>
+#include <linux/pci.h>
+
+static void cs5530a_warm_reset(struct pci_dev *dev)
+{
+       /* writing 1 to the reset control register, 0x44 causes the
+       cs5530a to perform a system warm reset */
+       pci_write_config_byte(dev, 0x44, 0x1);
+       udelay(50); /* shouldn't get here but be safe and spin-a-while */
+       return;
+}
+
+struct device_fixup {
+       unsigned int vendor;
+       unsigned int device;
+       void (*reboot_fixup)(struct pci_dev *);
+};
+
+static struct device_fixup fixups_table[] = {
+{ PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, cs5530a_warm_reset },
+};
+
+/*
+ * we see if any fixup is available for our current hardware. if there
+ * is a fixup, we call it and we expect to never return from it. if we
+ * do return, we keep looking and then eventually fall back to the
+ * standard mach_reboot on return.
+ */
+void mach_reboot_fixups(void)
+{
+       struct device_fixup *cur;
+       struct pci_dev *dev;
+       int i;
+
+       for (i=0; i < (sizeof(fixups_table)/sizeof(fixups_table[0])); i++) {
+               cur = &(fixups_table[i]);
+               dev = pci_get_device(cur->vendor, cur->device, 0);
+               if (!dev)
+                       continue;
+
+               cur->reboot_fixup(dev);
+       }
+
+       printk(KERN_WARNING "No reboot fixup found for your hardware\n");
+}
+
diff --git a/arch/ia64/sn/kernel/tiocx.c b/arch/ia64/sn/kernel/tiocx.c
new file mode 100644 (file)
index 0000000..ab9b5f3
--- /dev/null
@@ -0,0 +1,552 @@
+/*
+ * 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 Silicon Graphics, Inc.  All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/sn/sn_sal.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/io.h>
+#include <asm/sn/types.h>
+#include <asm/sn/shubio.h>
+#include <asm/sn/tiocx.h>
+#include <asm/sn/l1.h>
+#include <asm/sn/module.h>
+#include "tio.h"
+#include "xtalk/xwidgetdev.h"
+#include "xtalk/hubdev.h"
+
+#define CX_DEV_NONE 0
+#define DEVICE_NAME "tiocx"
+#define WIDGET_ID 0
+#define TIOCX_DEBUG 0
+
+#if TIOCX_DEBUG
+#define DBG(fmt...)    printk(KERN_ALERT fmt)
+#else
+#define DBG(fmt...)
+#endif
+
+struct device_attribute dev_attr_cxdev_control;
+
+/**
+ * tiocx_match - Try to match driver id list with device.
+ * @dev: device pointer
+ * @drv: driver pointer
+ *
+ * Returns 1 if match, 0 otherwise.
+ */
+static int tiocx_match(struct device *dev, struct device_driver *drv)
+{
+       struct cx_dev *cx_dev = to_cx_dev(dev);
+       struct cx_drv *cx_drv = to_cx_driver(drv);
+       const struct cx_device_id *ids = cx_drv->id_table;
+
+       if (!ids)
+               return 0;
+
+       while (ids->part_num) {
+               if (ids->part_num == cx_dev->cx_id.part_num)
+                       return 1;
+               ids++;
+       }
+       return 0;
+
+}
+
+static int tiocx_hotplug(struct device *dev, char **envp, int num_envp,
+                        char *buffer, int buffer_size)
+{
+       return -ENODEV;
+}
+
+static void tiocx_bus_release(struct device *dev)
+{
+       kfree(to_cx_dev(dev));
+}
+
+struct bus_type tiocx_bus_type = {
+       .name = "tiocx",
+       .match = tiocx_match,
+       .hotplug = tiocx_hotplug,
+};
+
+/**
+ * cx_device_match - Find cx_device in the id table.
+ * @ids: id table from driver
+ * @cx_device: part/mfg id for the device
+ *
+ */
+static const struct cx_device_id *cx_device_match(const struct cx_device_id
+                                                 *ids,
+                                                 struct cx_dev *cx_device)
+{
+       /*
+        * NOTES: We may want to check for CX_ANY_ID too.
+        *        Do we want to match against nasid too?
+        *        CX_DEV_NONE == 0, if the driver tries to register for
+        *        part/mfg == 0 we should return no-match (NULL) here.
+        */
+       while (ids->part_num && ids->mfg_num) {
+               if (ids->part_num == cx_device->cx_id.part_num &&
+                   ids->mfg_num == cx_device->cx_id.mfg_num)
+                       return ids;
+               ids++;
+       }
+
+       return NULL;
+}
+
+/**
+ * cx_device_probe - Look for matching device.
+ *                     Call driver probe routine if found.
+ * @cx_driver: driver table (cx_drv struct) from driver
+ * @cx_device: part/mfg id for the device
+ */
+static int cx_device_probe(struct device *dev)
+{
+       const struct cx_device_id *id;
+       struct cx_drv *cx_drv = to_cx_driver(dev->driver);
+       struct cx_dev *cx_dev = to_cx_dev(dev);
+       int error = 0;
+
+       if (!cx_dev->driver && cx_drv->probe) {
+               id = cx_device_match(cx_drv->id_table, cx_dev);
+               if (id) {
+                       if ((error = cx_drv->probe(cx_dev, id)) < 0)
+                               return error;
+                       else
+                               cx_dev->driver = cx_drv;
+               }
+       }
+
+       return error;
+}
+
+/**
+ * cx_driver_remove - Remove driver from device struct.
+ * @dev: device
+ */
+static int cx_driver_remove(struct device *dev)
+{
+       struct cx_dev *cx_dev = to_cx_dev(dev);
+       struct cx_drv *cx_drv = cx_dev->driver;
+       if (cx_drv->remove)
+               cx_drv->remove(cx_dev);
+       cx_dev->driver = NULL;
+       return 0;
+}
+
+/**
+ * cx_driver_register - Register the driver.
+ * @cx_driver: driver table (cx_drv struct) from driver
+ * 
+ * Called from the driver init routine to register a driver.
+ * The cx_drv struct contains the driver name, a pointer to
+ * a table of part/mfg numbers and a pointer to the driver's
+ * probe/attach routine.
+ */
+int cx_driver_register(struct cx_drv *cx_driver)
+{
+       cx_driver->driver.name = cx_driver->name;
+       cx_driver->driver.bus = &tiocx_bus_type;
+       cx_driver->driver.probe = cx_device_probe;
+       cx_driver->driver.remove = cx_driver_remove;
+
+       return driver_register(&cx_driver->driver);
+}
+
+/**
+ * cx_driver_unregister - Unregister the driver.
+ * @cx_driver: driver table (cx_drv struct) from driver
+ */
+int cx_driver_unregister(struct cx_drv *cx_driver)
+{
+       driver_unregister(&cx_driver->driver);
+       return 0;
+}
+
+/**
+ * cx_device_register - Register a device.
+ * @nasid: device's nasid
+ * @part_num: device's part number
+ * @mfg_num: device's manufacturer number
+ * @hubdev: hub info associated with this device
+ *
+ */
+int
+cx_device_register(nasid_t nasid, int part_num, int mfg_num,
+                  struct hubdev_info *hubdev)
+{
+       struct cx_dev *cx_dev;
+
+       cx_dev = kcalloc(1, sizeof(struct cx_dev), GFP_KERNEL);
+       DBG("cx_dev= 0x%p\n", cx_dev);
+       if (cx_dev == NULL)
+               return -ENOMEM;
+
+       cx_dev->cx_id.part_num = part_num;
+       cx_dev->cx_id.mfg_num = mfg_num;
+       cx_dev->cx_id.nasid = nasid;
+       cx_dev->hubdev = hubdev;
+
+       cx_dev->dev.parent = NULL;
+       cx_dev->dev.bus = &tiocx_bus_type;
+       cx_dev->dev.release = tiocx_bus_release;
+       snprintf(cx_dev->dev.bus_id, BUS_ID_SIZE, "%d.0x%x",
+                cx_dev->cx_id.nasid, cx_dev->cx_id.part_num);
+       device_register(&cx_dev->dev);
+       get_device(&cx_dev->dev);
+
+       device_create_file(&cx_dev->dev, &dev_attr_cxdev_control);
+
+       return 0;
+}
+
+/**
+ * cx_device_unregister - Unregister a device.
+ * @cx_dev: part/mfg id for the device
+ */
+int cx_device_unregister(struct cx_dev *cx_dev)
+{
+       put_device(&cx_dev->dev);
+       device_unregister(&cx_dev->dev);
+       return 0;
+}
+
+/**
+ * cx_device_reload - Reload the device.
+ * @nasid: device's nasid
+ * @part_num: device's part number
+ * @mfg_num: device's manufacturer number
+ *
+ * Remove the device associated with 'nasid' from device list and then
+ * call device-register with the given part/mfg numbers.
+ */
+static int cx_device_reload(struct cx_dev *cx_dev)
+{
+       device_remove_file(&cx_dev->dev, &dev_attr_cxdev_control);
+       cx_device_unregister(cx_dev);
+       return cx_device_register(cx_dev->cx_id.nasid, cx_dev->cx_id.part_num,
+                                 cx_dev->cx_id.mfg_num, cx_dev->hubdev);
+}
+
+static inline uint64_t tiocx_intr_alloc(nasid_t nasid, int widget,
+                                       u64 sn_irq_info,
+                                       int req_irq, nasid_t req_nasid,
+                                       int req_slice)
+{
+       struct ia64_sal_retval rv;
+       rv.status = 0;
+       rv.v0 = 0;
+
+       ia64_sal_oemcall_nolock(&rv, SN_SAL_IOIF_INTERRUPT,
+                               SAL_INTR_ALLOC, nasid,
+                               widget, sn_irq_info, req_irq,
+                               req_nasid, req_slice);
+       return rv.status;
+}
+
+static inline void tiocx_intr_free(nasid_t nasid, int widget,
+                                  struct sn_irq_info *sn_irq_info)
+{
+       struct ia64_sal_retval rv;
+       rv.status = 0;
+       rv.v0 = 0;
+
+       ia64_sal_oemcall_nolock(&rv, SN_SAL_IOIF_INTERRUPT,
+                               SAL_INTR_FREE, nasid,
+                               widget, sn_irq_info->irq_irq,
+                               sn_irq_info->irq_cookie, 0, 0);
+}
+
+struct sn_irq_info *tiocx_irq_alloc(nasid_t nasid, int widget, int irq,
+                                   nasid_t req_nasid, int slice)
+{
+       struct sn_irq_info *sn_irq_info;
+       int status;
+       int sn_irq_size = sizeof(struct sn_irq_info);
+
+       if ((nasid & 1) == 0)
+               return NULL;
+
+       sn_irq_info = kmalloc(sn_irq_size, GFP_KERNEL);
+       if (sn_irq_info == NULL)
+               return NULL;
+
+       memset(sn_irq_info, 0x0, sn_irq_size);
+
+       status = tiocx_intr_alloc(nasid, widget, __pa(sn_irq_info), irq,
+                                 req_nasid, slice);
+       if (status) {
+               kfree(sn_irq_info);
+               return NULL;
+       } else {
+               return sn_irq_info;
+       }
+}
+
+void tiocx_irq_free(struct sn_irq_info *sn_irq_info)
+{
+       uint64_t bridge = (uint64_t) sn_irq_info->irq_bridge;
+       nasid_t nasid = NASID_GET(bridge);
+       int widget;
+
+       if (nasid & 1) {
+               widget = TIO_SWIN_WIDGETNUM(bridge);
+               tiocx_intr_free(nasid, widget, sn_irq_info);
+               kfree(sn_irq_info);
+       }
+}
+
+uint64_t tiocx_dma_addr(uint64_t addr)
+{
+       return PHYS_TO_TIODMA(addr);
+}
+
+uint64_t tiocx_swin_base(int nasid)
+{
+       return TIO_SWIN_BASE(nasid, TIOCX_CORELET);
+}
+
+EXPORT_SYMBOL(cx_driver_register);
+EXPORT_SYMBOL(cx_driver_unregister);
+EXPORT_SYMBOL(cx_device_register);
+EXPORT_SYMBOL(cx_device_unregister);
+EXPORT_SYMBOL(tiocx_irq_alloc);
+EXPORT_SYMBOL(tiocx_irq_free);
+EXPORT_SYMBOL(tiocx_bus_type);
+EXPORT_SYMBOL(tiocx_dma_addr);
+EXPORT_SYMBOL(tiocx_swin_base);
+
+static void tio_conveyor_set(nasid_t nasid, int enable_flag)
+{
+       uint64_t ice_frz;
+       uint64_t disable_cb = (1ull << 61);
+
+       if (!(nasid & 1))
+               return;
+
+       ice_frz = REMOTE_HUB_L(nasid, TIO_ICE_FRZ_CFG);
+       if (enable_flag) {
+               if (!(ice_frz & disable_cb))    /* already enabled */
+                       return;
+               ice_frz &= ~disable_cb;
+       } else {
+               if (ice_frz & disable_cb)       /* already disabled */
+                       return;
+               ice_frz |= disable_cb;
+       }
+       DBG(KERN_ALERT "TIO_ICE_FRZ_CFG= 0x%lx\n", ice_frz);
+       REMOTE_HUB_S(nasid, TIO_ICE_FRZ_CFG, ice_frz);
+}
+
+#define tio_conveyor_enable(nasid) tio_conveyor_set(nasid, 1)
+#define tio_conveyor_disable(nasid) tio_conveyor_set(nasid, 0)
+
+static void tio_corelet_reset(nasid_t nasid, int corelet)
+{
+       if (!(nasid & 1))
+               return;
+
+       REMOTE_HUB_S(nasid, TIO_ICE_PMI_TX_CFG, 1 << corelet);
+       udelay(2000);
+       REMOTE_HUB_S(nasid, TIO_ICE_PMI_TX_CFG, 0);
+       udelay(2000);
+}
+
+static int tiocx_btchar_get(int nasid)
+{
+       moduleid_t module_id;
+       geoid_t geoid;
+       int cnodeid;
+
+       cnodeid = nasid_to_cnodeid(nasid);
+       geoid = cnodeid_get_geoid(cnodeid);
+       module_id = geo_module(geoid);
+       return MODULE_GET_BTCHAR(module_id);
+}
+
+static int is_fpga_brick(int nasid)
+{
+       switch (tiocx_btchar_get(nasid)) {
+       case L1_BRICKTYPE_SA:
+       case L1_BRICKTYPE_ATHENA:
+               return 1;
+       }
+       return 0;
+}
+
+static int bitstream_loaded(nasid_t nasid)
+{
+       uint64_t cx_credits;
+
+       cx_credits = REMOTE_HUB_L(nasid, TIO_ICE_PMI_TX_DYN_CREDIT_STAT_CB3);
+       cx_credits &= TIO_ICE_PMI_TX_DYN_CREDIT_STAT_CB3_CREDIT_CNT_MASK;
+       DBG("cx_credits= 0x%lx\n", cx_credits);
+
+       return (cx_credits == 0xf) ? 1 : 0;
+}
+
+static int tiocx_reload(struct cx_dev *cx_dev)
+{
+       int part_num = CX_DEV_NONE;
+       int mfg_num = CX_DEV_NONE;
+       nasid_t nasid = cx_dev->cx_id.nasid;
+
+       if (bitstream_loaded(nasid)) {
+               uint64_t cx_id;
+
+               cx_id =
+                   *(volatile int32_t *)(TIO_SWIN_BASE(nasid, TIOCX_CORELET) +
+                                         WIDGET_ID);
+               part_num = XWIDGET_PART_NUM(cx_id);
+               mfg_num = XWIDGET_MFG_NUM(cx_id);
+               DBG("part= 0x%x, mfg= 0x%x\n", part_num, mfg_num);
+               /* just ignore it if it's a CE */
+               if (part_num == TIO_CE_ASIC_PARTNUM)
+                       return 0;
+       }
+
+       cx_dev->cx_id.part_num = part_num;
+       cx_dev->cx_id.mfg_num = mfg_num;
+
+       /*
+        * Delete old device and register the new one.  It's ok if
+        * part_num/mfg_num == CX_DEV_NONE.  We want to register
+        * devices in the table even if a bitstream isn't loaded.
+        * That allows use to see that a bitstream isn't loaded via
+        * TIOCX_IOCTL_DEV_LIST.
+        */
+       return cx_device_reload(cx_dev);
+}
+
+static ssize_t show_cxdev_control(struct device *dev, char *buf)
+{
+       struct cx_dev *cx_dev = to_cx_dev(dev);
+
+       return sprintf(buf, "0x%x 0x%x 0x%x %d\n",
+                      cx_dev->cx_id.nasid,
+                      cx_dev->cx_id.part_num, cx_dev->cx_id.mfg_num,
+                      tiocx_btchar_get(cx_dev->cx_id.nasid));
+}
+
+static ssize_t store_cxdev_control(struct device *dev, const char *buf,
+                                  size_t count)
+{
+       int n;
+       struct cx_dev *cx_dev = to_cx_dev(dev);
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (count <= 0)
+               return 0;
+
+       n = simple_strtoul(buf, NULL, 0);
+
+       switch (n) {
+       case 1:
+               tiocx_reload(cx_dev);
+               break;
+       case 3:
+               tio_corelet_reset(cx_dev->cx_id.nasid, TIOCX_CORELET);
+               break;
+       default:
+               break;
+       }
+
+       return count;
+}
+
+DEVICE_ATTR(cxdev_control, 0644, show_cxdev_control, store_cxdev_control);
+
+static int __init tiocx_init(void)
+{
+       cnodeid_t cnodeid;
+       int found_tiocx_device = 0;
+
+       bus_register(&tiocx_bus_type);
+
+       for (cnodeid = 0; cnodeid < MAX_COMPACT_NODES; cnodeid++) {
+               nasid_t nasid;
+
+               if ((nasid = cnodeid_to_nasid(cnodeid)) < 0)
+                       break;  /* No more nasids .. bail out of loop */
+
+               if ((nasid & 0x1) && is_fpga_brick(nasid)) {
+                       struct hubdev_info *hubdev;
+                       struct xwidget_info *widgetp;
+
+                       DBG("Found TIO at nasid 0x%x\n", nasid);
+
+                       hubdev =
+                           (struct hubdev_info *)(NODEPDA(cnodeid)->pdinfo);
+
+                       widgetp = &hubdev->hdi_xwidget_info[TIOCX_CORELET];
+
+                       /* The CE hangs off of the CX port but is not an FPGA */
+                       if (widgetp->xwi_hwid.part_num == TIO_CE_ASIC_PARTNUM)
+                               continue;
+
+                       tio_corelet_reset(nasid, TIOCX_CORELET);
+                       tio_conveyor_enable(nasid);
+
+                       if (cx_device_register
+                           (nasid, widgetp->xwi_hwid.part_num,
+                            widgetp->xwi_hwid.mfg_num, hubdev) < 0)
+                               return -ENXIO;
+                       else
+                               found_tiocx_device++;
+               }
+       }
+
+       /* It's ok if we find zero devices. */
+       DBG("found_tiocx_device= %d\n", found_tiocx_device);
+
+       return 0;
+}
+
+static void __exit tiocx_exit(void)
+{
+       struct device *dev;
+       struct device *tdev;
+
+       DBG("tiocx_exit\n");
+
+       /*
+        * Unregister devices.
+        */
+       list_for_each_entry_safe(dev, tdev, &tiocx_bus_type.devices.list,
+                                bus_list) {
+               if (dev) {
+                       struct cx_dev *cx_dev = to_cx_dev(dev);
+                       device_remove_file(dev, &dev_attr_cxdev_control);
+                       cx_device_unregister(cx_dev);
+               }
+       }
+
+       bus_unregister(&tiocx_bus_type);
+}
+
+module_init(tiocx_init);
+module_exit(tiocx_exit);
+
+/************************************************************************
+ * Module licensing and description
+ ************************************************************************/
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bruce Losure <blosure@sgi.com>");
+MODULE_DESCRIPTION("TIOCX module");
+MODULE_SUPPORTED_DEVICE(DEVICE_NAME);
diff --git a/arch/ia64/sn/kernel/xpc.h b/arch/ia64/sn/kernel/xpc.h
new file mode 100644 (file)
index 0000000..1a0aed8
--- /dev/null
@@ -0,0 +1,991 @@
+/*
+ * 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-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+
+/*
+ * Cross Partition Communication (XPC) structures and macros.
+ */
+
+#ifndef _IA64_SN_KERNEL_XPC_H
+#define _IA64_SN_KERNEL_XPC_H
+
+
+#include <linux/config.h>
+#include <linux/interrupt.h>
+#include <linux/sysctl.h>
+#include <linux/device.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/sn/bte.h>
+#include <asm/sn/clksupport.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/mspec.h>
+#include <asm/sn/shub_mmr.h>
+#include <asm/sn/xp.h>
+
+
+/*
+ * XPC Version numbers consist of a major and minor number. XPC can always
+ * talk to versions with same major #, and never talk to versions with a
+ * different major #.
+ */
+#define _XPC_VERSION(_maj, _min)       (((_maj) << 4) | ((_min) & 0xf))
+#define XPC_VERSION_MAJOR(_v)          ((_v) >> 4)
+#define XPC_VERSION_MINOR(_v)          ((_v) & 0xf)
+
+
+/*
+ * The next macros define word or bit representations for given
+ * C-brick nasid in either the SAL provided bit array representing
+ * nasids in the partition/machine or the AMO_t array used for
+ * inter-partition initiation communications.
+ *
+ * For SN2 machines, C-Bricks are alway even numbered NASIDs.  As
+ * such, some space will be saved by insisting that nasid information
+ * passed from SAL always be packed for C-Bricks and the
+ * cross-partition interrupts use the same packing scheme.
+ */
+#define XPC_NASID_W_INDEX(_n)  (((_n) / 64) / 2)
+#define XPC_NASID_B_INDEX(_n)  (((_n) / 2) & (64 - 1))
+#define XPC_NASID_IN_ARRAY(_n, _p) ((_p)[XPC_NASID_W_INDEX(_n)] & \
+                                   (1UL << XPC_NASID_B_INDEX(_n)))
+#define XPC_NASID_FROM_W_B(_w, _b) (((_w) * 64 + (_b)) * 2)
+
+#define XPC_HB_DEFAULT_INTERVAL                5       /* incr HB every x secs */
+#define XPC_HB_CHECK_DEFAULT_TIMEOUT   20      /* check HB every x secs */
+
+/* define the process name of HB checker and the CPU it is pinned to */
+#define XPC_HB_CHECK_THREAD_NAME       "xpc_hb"
+#define XPC_HB_CHECK_CPU               0
+
+/* define the process name of the discovery thread */
+#define XPC_DISCOVERY_THREAD_NAME      "xpc_discovery"
+
+
+#define XPC_HB_ALLOWED(_p, _v) ((_v)->heartbeating_to_mask & (1UL << (_p)))
+#define XPC_ALLOW_HB(_p, _v)   (_v)->heartbeating_to_mask |= (1UL << (_p))
+#define XPC_DISALLOW_HB(_p, _v)        (_v)->heartbeating_to_mask &= (~(1UL << (_p)))
+
+
+/*
+ * Reserved Page provided by SAL.
+ *
+ * SAL provides one page per partition of reserved memory.  When SAL
+ * initialization is complete, SAL_signature, SAL_version, partid,
+ * part_nasids, and mach_nasids are set.
+ *
+ * Note: Until vars_pa is set, the partition XPC code has not been initialized.
+ */
+struct xpc_rsvd_page {
+       u64 SAL_signature;      /* SAL unique signature */
+       u64 SAL_version;        /* SAL specified version */
+       u8 partid;              /* partition ID from SAL */
+       u8 version;
+       u8 pad[6];              /* pad to u64 align */
+       u64 vars_pa;
+       u64 part_nasids[XP_NASID_MASK_WORDS] ____cacheline_aligned;
+       u64 mach_nasids[XP_NASID_MASK_WORDS] ____cacheline_aligned;
+};
+#define XPC_RP_VERSION _XPC_VERSION(1,0) /* version 1.0 of the reserved page */
+
+#define XPC_RSVD_PAGE_ALIGNED_SIZE \
+                       (L1_CACHE_ALIGN(sizeof(struct xpc_rsvd_page)))
+
+
+/*
+ * Define the structures by which XPC variables can be exported to other
+ * partitions. (There are two: struct xpc_vars and struct xpc_vars_part)
+ */
+
+/*
+ * The following structure describes the partition generic variables
+ * needed by other partitions in order to properly initialize.
+ *
+ * struct xpc_vars version number also applies to struct xpc_vars_part.
+ * Changes to either structure and/or related functionality should be
+ * reflected by incrementing either the major or minor version numbers
+ * of struct xpc_vars.
+ */
+struct xpc_vars {
+       u8 version;
+       u64 heartbeat;
+       u64 heartbeating_to_mask;
+       u64 kdb_status;         /* 0 = machine running */
+       int act_nasid;
+       int act_phys_cpuid;
+       u64 vars_part_pa;
+       u64 amos_page_pa;       /* paddr of page of AMOs from MSPEC driver */
+       AMO_t *amos_page;       /* vaddr of page of AMOs from MSPEC driver */
+       AMO_t *act_amos;        /* pointer to the first activation AMO */
+};
+#define XPC_V_VERSION _XPC_VERSION(3,0) /* version 3.0 of the cross vars */
+
+#define XPC_VARS_ALIGNED_SIZE  (L1_CACHE_ALIGN(sizeof(struct xpc_vars)))
+
+/*
+ * The following structure describes the per partition specific variables.
+ *
+ * An array of these structures, one per partition, will be defined. As a
+ * partition becomes active XPC will copy the array entry corresponding to
+ * itself from that partition. It is desirable that the size of this
+ * structure evenly divide into a cacheline, such that none of the entries
+ * in this array crosses a cacheline boundary. As it is now, each entry
+ * occupies half a cacheline.
+ */
+struct xpc_vars_part {
+       u64 magic;
+
+       u64 openclose_args_pa;  /* physical address of open and close args */
+       u64 GPs_pa;             /* physical address of Get/Put values */
+
+       u64 IPI_amo_pa;         /* physical address of IPI AMO_t structure */
+       int IPI_nasid;          /* nasid of where to send IPIs */
+       int IPI_phys_cpuid;     /* physical CPU ID of where to send IPIs */
+
+       u8 nchannels;           /* #of defined channels supported */
+
+       u8 reserved[23];        /* pad to a full 64 bytes */
+};
+
+/*
+ * The vars_part MAGIC numbers play a part in the first contact protocol.
+ *
+ * MAGIC1 indicates that the per partition specific variables for a remote
+ * partition have been initialized by this partition.
+ *
+ * MAGIC2 indicates that this partition has pulled the remote partititions
+ * per partition variables that pertain to this partition.
+ */
+#define XPC_VP_MAGIC1  0x0053524156435058L  /* 'XPCVARS\0'L (little endian) */
+#define XPC_VP_MAGIC2  0x0073726176435058L  /* 'XPCvars\0'L (little endian) */
+
+
+
+/*
+ * Functions registered by add_timer() or called by kernel_thread() only
+ * allow for a single 64-bit argument. The following macros can be used to
+ * pack and unpack two (32-bit, 16-bit or 8-bit) arguments into or out from
+ * the passed argument.
+ */
+#define XPC_PACK_ARGS(_arg1, _arg2) \
+                       ((((u64) _arg1) & 0xffffffff) | \
+                       ((((u64) _arg2) & 0xffffffff) << 32))
+
+#define XPC_UNPACK_ARG1(_args) (((u64) _args) & 0xffffffff)
+#define XPC_UNPACK_ARG2(_args) ((((u64) _args) >> 32) & 0xffffffff)
+
+
+
+/*
+ * Define a Get/Put value pair (pointers) used with a message queue.
+ */
+struct xpc_gp {
+       s64 get;        /* Get value */
+       s64 put;        /* Put value */
+};
+
+#define XPC_GP_SIZE \
+               L1_CACHE_ALIGN(sizeof(struct xpc_gp) * XPC_NCHANNELS)
+
+
+
+/*
+ * Define a structure that contains arguments associated with opening and
+ * closing a channel.
+ */
+struct xpc_openclose_args {
+       u16 reason;             /* reason why channel is closing */
+       u16 msg_size;           /* sizeof each message entry */
+       u16 remote_nentries;    /* #of message entries in remote msg queue */
+       u16 local_nentries;     /* #of message entries in local msg queue */
+       u64 local_msgqueue_pa;  /* physical address of local message queue */
+};
+
+#define XPC_OPENCLOSE_ARGS_SIZE \
+             L1_CACHE_ALIGN(sizeof(struct xpc_openclose_args) * XPC_NCHANNELS)
+
+
+
+/* struct xpc_msg flags */
+
+#define        XPC_M_DONE              0x01    /* msg has been received/consumed */
+#define        XPC_M_READY             0x02    /* msg is ready to be sent */
+#define        XPC_M_INTERRUPT         0x04    /* send interrupt when msg consumed */
+
+
+#define XPC_MSG_ADDRESS(_payload) \
+               ((struct xpc_msg *)((u8 *)(_payload) - XPC_MSG_PAYLOAD_OFFSET))
+
+
+
+/*
+ * Defines notify entry.
+ *
+ * This is used to notify a message's sender that their message was received
+ * and consumed by the intended recipient.
+ */
+struct xpc_notify {
+       struct semaphore sema;          /* notify semaphore */
+       u8 type;                        /* type of notification */
+
+       /* the following two fields are only used if type == XPC_N_CALL */
+       xpc_notify_func func;           /* user's notify function */
+       void *key;                      /* pointer to user's key */
+};
+
+/* struct xpc_notify type of notification */
+
+#define        XPC_N_CALL              0x01    /* notify function provided by user */
+
+
+
+/*
+ * Define the structure that manages all the stuff required by a channel. In
+ * particular, they are used to manage the messages sent across the channel.
+ *
+ * This structure is private to a partition, and is NOT shared across the
+ * partition boundary.
+ *
+ * There is an array of these structures for each remote partition. It is
+ * allocated at the time a partition becomes active. The array contains one
+ * of these structures for each potential channel connection to that partition.
+ *
+ * Each of these structures manages two message queues (circular buffers).
+ * They are allocated at the time a channel connection is made. One of
+ * these message queues (local_msgqueue) holds the locally created messages
+ * that are destined for the remote partition. The other of these message
+ * queues (remote_msgqueue) is a locally cached copy of the remote partition's
+ * own local_msgqueue.
+ *
+ * The following is a description of the Get/Put pointers used to manage these
+ * two message queues. Consider the local_msgqueue to be on one partition
+ * and the remote_msgqueue to be its cached copy on another partition. A
+ * description of what each of the lettered areas contains is included.
+ *
+ *
+ *                     local_msgqueue      remote_msgqueue
+ *
+ *                        |/////////|      |/////////|
+ *    w_remote_GP.get --> +---------+      |/////////|
+ *                        |    F    |      |/////////|
+ *     remote_GP.get  --> +---------+      +---------+ <-- local_GP->get
+ *                        |         |      |         |
+ *                        |         |      |    E    |
+ *                        |         |      |         |
+ *                        |         |      +---------+ <-- w_local_GP.get
+ *                        |    B    |      |/////////|
+ *                        |         |      |////D////|
+ *                        |         |      |/////////|
+ *                        |         |      +---------+ <-- w_remote_GP.put
+ *                        |         |      |////C////|
+ *      local_GP->put --> +---------+      +---------+ <-- remote_GP.put
+ *                        |         |      |/////////|
+ *                        |    A    |      |/////////|
+ *                        |         |      |/////////|
+ *     w_local_GP.put --> +---------+      |/////////|
+ *                        |/////////|      |/////////|
+ *
+ *
+ *         ( remote_GP.[get|put] are cached copies of the remote
+ *           partition's local_GP->[get|put], and thus their values can
+ *           lag behind their counterparts on the remote partition. )
+ *
+ *
+ *  A - Messages that have been allocated, but have not yet been sent to the
+ *     remote partition.
+ *
+ *  B - Messages that have been sent, but have not yet been acknowledged by the
+ *      remote partition as having been received.
+ *
+ *  C - Area that needs to be prepared for the copying of sent messages, by
+ *     the clearing of the message flags of any previously received messages.
+ *
+ *  D - Area into which sent messages are to be copied from the remote
+ *     partition's local_msgqueue and then delivered to their intended
+ *     recipients. [ To allow for a multi-message copy, another pointer
+ *     (next_msg_to_pull) has been added to keep track of the next message
+ *     number needing to be copied (pulled). It chases after w_remote_GP.put.
+ *     Any messages lying between w_local_GP.get and next_msg_to_pull have
+ *     been copied and are ready to be delivered. ]
+ *
+ *  E - Messages that have been copied and delivered, but have not yet been
+ *     acknowledged by the recipient as having been received.
+ *
+ *  F - Messages that have been acknowledged, but XPC has not yet notified the
+ *     sender that the message was received by its intended recipient.
+ *     This is also an area that needs to be prepared for the allocating of
+ *     new messages, by the clearing of the message flags of the acknowledged
+ *     messages.
+ */
+struct xpc_channel {
+       partid_t partid;                /* ID of remote partition connected */
+       spinlock_t lock;                /* lock for updating this structure */
+       u32 flags;                      /* general flags */
+
+       enum xpc_retval reason;         /* reason why channel is disconnect'g */
+       int reason_line;                /* line# disconnect initiated from */
+
+       u16 number;                     /* channel # */
+
+       u16 msg_size;                   /* sizeof each msg entry */
+       u16 local_nentries;             /* #of msg entries in local msg queue */
+       u16 remote_nentries;            /* #of msg entries in remote msg queue*/
+
+       void *local_msgqueue_base;      /* base address of kmalloc'd space */
+       struct xpc_msg *local_msgqueue; /* local message queue */
+       void *remote_msgqueue_base;     /* base address of kmalloc'd space */
+       struct xpc_msg *remote_msgqueue;/* cached copy of remote partition's */
+                                       /* local message queue */
+       u64 remote_msgqueue_pa;         /* phys addr of remote partition's */
+                                       /* local message queue */
+
+       atomic_t references;            /* #of external references to queues */
+
+       atomic_t n_on_msg_allocate_wq;   /* #on msg allocation wait queue */
+       wait_queue_head_t msg_allocate_wq; /* msg allocation wait queue */
+
+       /* queue of msg senders who want to be notified when msg received */
+
+       atomic_t n_to_notify;           /* #of msg senders to notify */
+       struct xpc_notify *notify_queue;/* notify queue for messages sent */
+
+       xpc_channel_func func;          /* user's channel function */
+       void *key;                      /* pointer to user's key */
+
+       struct semaphore msg_to_pull_sema; /* next msg to pull serialization */
+       struct semaphore teardown_sema;    /* wait for teardown completion */
+
+       struct xpc_openclose_args *local_openclose_args; /* args passed on */
+                                       /* opening or closing of channel */
+
+       /* various flavors of local and remote Get/Put values */
+
+       struct xpc_gp *local_GP;        /* local Get/Put values */
+       struct xpc_gp remote_GP;        /* remote Get/Put values */
+       struct xpc_gp w_local_GP;       /* working local Get/Put values */
+       struct xpc_gp w_remote_GP;      /* working remote Get/Put values */
+       s64 next_msg_to_pull;           /* Put value of next msg to pull */
+
+       /* kthread management related fields */
+
+// >>> rethink having kthreads_assigned_limit and kthreads_idle_limit; perhaps
+// >>> allow the assigned limit be unbounded and let the idle limit be dynamic
+// >>> dependent on activity over the last interval of time
+       atomic_t kthreads_assigned;     /* #of kthreads assigned to channel */
+       u32 kthreads_assigned_limit;    /* limit on #of kthreads assigned */
+       atomic_t kthreads_idle;         /* #of kthreads idle waiting for work */
+       u32 kthreads_idle_limit;        /* limit on #of kthreads idle */
+       atomic_t kthreads_active;       /* #of kthreads actively working */
+       // >>> following field is temporary
+       u32 kthreads_created;           /* total #of kthreads created */
+
+       wait_queue_head_t idle_wq;      /* idle kthread wait queue */
+
+} ____cacheline_aligned;
+
+
+/* struct xpc_channel flags */
+
+#define        XPC_C_WASCONNECTED      0x00000001 /* channel was connected */
+
+#define        XPC_C_ROPENREPLY        0x00000002 /* remote open channel reply */
+#define        XPC_C_OPENREPLY         0x00000004 /* local open channel reply */
+#define        XPC_C_ROPENREQUEST      0x00000008 /* remote open channel request */
+#define        XPC_C_OPENREQUEST       0x00000010 /* local open channel request */
+
+#define        XPC_C_SETUP             0x00000020 /* channel's msgqueues are alloc'd */
+#define        XPC_C_CONNECTCALLOUT    0x00000040 /* channel connected callout made */
+#define        XPC_C_CONNECTED         0x00000080 /* local channel is connected */
+#define        XPC_C_CONNECTING        0x00000100 /* channel is being connected */
+
+#define        XPC_C_RCLOSEREPLY       0x00000200 /* remote close channel reply */
+#define        XPC_C_CLOSEREPLY        0x00000400 /* local close channel reply */
+#define        XPC_C_RCLOSEREQUEST     0x00000800 /* remote close channel request */
+#define        XPC_C_CLOSEREQUEST      0x00001000 /* local close channel request */
+
+#define        XPC_C_DISCONNECTED      0x00002000 /* channel is disconnected */
+#define        XPC_C_DISCONNECTING     0x00004000 /* channel is being disconnected */
+
+
+
+/*
+ * Manages channels on a partition basis. There is one of these structures
+ * for each partition (a partition will never utilize the structure that
+ * represents itself).
+ */
+struct xpc_partition {
+
+       /* XPC HB infrastructure */
+
+       u64 remote_rp_pa;               /* phys addr of partition's rsvd pg */
+       u64 remote_vars_pa;             /* phys addr of partition's vars */
+       u64 remote_vars_part_pa;        /* phys addr of partition's vars part */
+       u64 last_heartbeat;             /* HB at last read */
+       u64 remote_amos_page_pa;        /* phys addr of partition's amos page */
+       int remote_act_nasid;           /* active part's act/deact nasid */
+       int remote_act_phys_cpuid;      /* active part's act/deact phys cpuid */
+       u32 act_IRQ_rcvd;               /* IRQs since activation */
+       spinlock_t act_lock;            /* protect updating of act_state */
+       u8 act_state;                   /* from XPC HB viewpoint */
+       enum xpc_retval reason;         /* reason partition is deactivating */
+       int reason_line;                /* line# deactivation initiated from */
+       int reactivate_nasid;           /* nasid in partition to reactivate */
+
+
+       /* XPC infrastructure referencing and teardown control */
+
+       u8 setup_state;                 /* infrastructure setup state */
+       wait_queue_head_t teardown_wq;  /* kthread waiting to teardown infra */
+       atomic_t references;            /* #of references to infrastructure */
+
+
+       /*
+        * NONE OF THE PRECEDING FIELDS OF THIS STRUCTURE WILL BE CLEARED WHEN
+        * XPC SETS UP THE NECESSARY INFRASTRUCTURE TO SUPPORT CROSS PARTITION
+        * COMMUNICATION. ALL OF THE FOLLOWING FIELDS WILL BE CLEARED. (THE
+        * 'nchannels' FIELD MUST BE THE FIRST OF THE FIELDS TO BE CLEARED.)
+        */
+
+
+       u8 nchannels;              /* #of defined channels supported */
+       atomic_t nchannels_active; /* #of channels that are not DISCONNECTED */
+       struct xpc_channel *channels;/* array of channel structures */
+
+       void *local_GPs_base;     /* base address of kmalloc'd space */
+       struct xpc_gp *local_GPs; /* local Get/Put values */
+       void *remote_GPs_base;    /* base address of kmalloc'd space */
+       struct xpc_gp *remote_GPs;/* copy of remote partition's local Get/Put */
+                                 /* values */
+       u64 remote_GPs_pa;        /* phys address of remote partition's local */
+                                 /* Get/Put values */
+
+
+       /* fields used to pass args when opening or closing a channel */
+
+       void *local_openclose_args_base;  /* base address of kmalloc'd space */
+       struct xpc_openclose_args *local_openclose_args;  /* local's args */
+       void *remote_openclose_args_base; /* base address of kmalloc'd space */
+       struct xpc_openclose_args *remote_openclose_args; /* copy of remote's */
+                                         /* args */
+       u64 remote_openclose_args_pa;     /* phys addr of remote's args */
+
+
+       /* IPI sending, receiving and handling related fields */
+
+       int remote_IPI_nasid;       /* nasid of where to send IPIs */
+       int remote_IPI_phys_cpuid;  /* phys CPU ID of where to send IPIs */
+       AMO_t *remote_IPI_amo_va;   /* address of remote IPI AMO_t structure */
+
+       AMO_t *local_IPI_amo_va;    /* address of IPI AMO_t structure */
+       u64 local_IPI_amo;          /* IPI amo flags yet to be handled */
+       char IPI_owner[8];          /* IPI owner's name */
+       struct timer_list dropped_IPI_timer; /* dropped IPI timer */
+
+       spinlock_t IPI_lock;        /* IPI handler lock */
+
+
+       /* channel manager related fields */
+
+       atomic_t channel_mgr_requests;  /* #of requests to activate chan mgr */
+       wait_queue_head_t channel_mgr_wq; /* channel mgr's wait queue */
+
+} ____cacheline_aligned;
+
+
+/* struct xpc_partition act_state values (for XPC HB) */
+
+#define        XPC_P_INACTIVE          0x00    /* partition is not active */
+#define XPC_P_ACTIVATION_REQ   0x01    /* created thread to activate */
+#define XPC_P_ACTIVATING       0x02    /* activation thread started */
+#define XPC_P_ACTIVE           0x03    /* xpc_partition_up() was called */
+#define XPC_P_DEACTIVATING     0x04    /* partition deactivation initiated */
+
+
+#define XPC_DEACTIVATE_PARTITION(_p, _reason) \
+                       xpc_deactivate_partition(__LINE__, (_p), (_reason))
+
+
+/* struct xpc_partition setup_state values */
+
+#define XPC_P_UNSET            0x00    /* infrastructure was never setup */
+#define XPC_P_SETUP            0x01    /* infrastructure is setup */
+#define XPC_P_WTEARDOWN                0x02    /* waiting to teardown infrastructure */
+#define XPC_P_TORNDOWN         0x03    /* infrastructure is torndown */
+
+
+/*
+ * struct xpc_partition IPI_timer #of seconds to wait before checking for
+ * dropped IPIs. These occur whenever an IPI amo write doesn't complete until
+ * after the IPI was received.
+ */
+#define XPC_P_DROPPED_IPI_WAIT (0.25 * HZ)
+
+
+#define XPC_PARTID(_p) ((partid_t) ((_p) - &xpc_partitions[0]))
+
+
+
+/* found in xp_main.c */
+extern struct xpc_registration xpc_registrations[];
+
+
+/* >>> found in xpc_main.c only */
+extern struct device *xpc_part;
+extern struct device *xpc_chan;
+extern irqreturn_t xpc_notify_IRQ_handler(int, void *, struct pt_regs *);
+extern void xpc_dropped_IPI_check(struct xpc_partition *);
+extern void xpc_activate_kthreads(struct xpc_channel *, int);
+extern void xpc_create_kthreads(struct xpc_channel *, int);
+extern void xpc_disconnect_wait(int);
+
+
+/* found in xpc_main.c and efi-xpc.c */
+extern void xpc_activate_partition(struct xpc_partition *);
+
+
+/* found in xpc_partition.c */
+extern int xpc_exiting;
+extern int xpc_hb_interval;
+extern int xpc_hb_check_interval;
+extern struct xpc_vars *xpc_vars;
+extern struct xpc_rsvd_page *xpc_rsvd_page;
+extern struct xpc_vars_part *xpc_vars_part;
+extern struct xpc_partition xpc_partitions[XP_MAX_PARTITIONS + 1];
+extern char xpc_remote_copy_buffer[];
+extern struct xpc_rsvd_page *xpc_rsvd_page_init(void);
+extern void xpc_allow_IPI_ops(void);
+extern void xpc_restrict_IPI_ops(void);
+extern int xpc_identify_act_IRQ_sender(void);
+extern enum xpc_retval xpc_mark_partition_active(struct xpc_partition *);
+extern void xpc_mark_partition_inactive(struct xpc_partition *);
+extern void xpc_discovery(void);
+extern void xpc_check_remote_hb(void);
+extern void xpc_deactivate_partition(const int, struct xpc_partition *,
+                                               enum xpc_retval);
+extern enum xpc_retval xpc_initiate_partid_to_nasids(partid_t, void *);
+
+
+/* found in xpc_channel.c */
+extern void xpc_initiate_connect(int);
+extern void xpc_initiate_disconnect(int);
+extern enum xpc_retval xpc_initiate_allocate(partid_t, int, u32, void **);
+extern enum xpc_retval xpc_initiate_send(partid_t, int, void *);
+extern enum xpc_retval xpc_initiate_send_notify(partid_t, int, void *,
+                                               xpc_notify_func, void *);
+extern void xpc_initiate_received(partid_t, int, void *);
+extern enum xpc_retval xpc_setup_infrastructure(struct xpc_partition *);
+extern enum xpc_retval xpc_pull_remote_vars_part(struct xpc_partition *);
+extern void xpc_process_channel_activity(struct xpc_partition *);
+extern void xpc_connected_callout(struct xpc_channel *);
+extern void xpc_deliver_msg(struct xpc_channel *);
+extern void xpc_disconnect_channel(const int, struct xpc_channel *,
+                                       enum xpc_retval, unsigned long *);
+extern void xpc_disconnected_callout(struct xpc_channel *);
+extern void xpc_partition_down(struct xpc_partition *, enum xpc_retval);
+extern void xpc_teardown_infrastructure(struct xpc_partition *);
+
+
+
+static inline void
+xpc_wakeup_channel_mgr(struct xpc_partition *part)
+{
+       if (atomic_inc_return(&part->channel_mgr_requests) == 1) {
+               wake_up(&part->channel_mgr_wq);
+       }
+}
+
+
+
+/*
+ * These next two inlines are used to keep us from tearing down a channel's
+ * msg queues while a thread may be referencing them.
+ */
+static inline void
+xpc_msgqueue_ref(struct xpc_channel *ch)
+{
+       atomic_inc(&ch->references);
+}
+
+static inline void
+xpc_msgqueue_deref(struct xpc_channel *ch)
+{
+       s32 refs = atomic_dec_return(&ch->references);
+
+       DBUG_ON(refs < 0);
+       if (refs == 0) {
+               xpc_wakeup_channel_mgr(&xpc_partitions[ch->partid]);
+       }
+}
+
+
+
+#define XPC_DISCONNECT_CHANNEL(_ch, _reason, _irqflgs) \
+               xpc_disconnect_channel(__LINE__, _ch, _reason, _irqflgs)
+
+
+/*
+ * These two inlines are used to keep us from tearing down a partition's
+ * setup infrastructure while a thread may be referencing it.
+ */
+static inline void
+xpc_part_deref(struct xpc_partition *part)
+{
+       s32 refs = atomic_dec_return(&part->references);
+
+
+       DBUG_ON(refs < 0);
+       if (refs == 0 && part->setup_state == XPC_P_WTEARDOWN) {
+               wake_up(&part->teardown_wq);
+       }
+}
+
+static inline int
+xpc_part_ref(struct xpc_partition *part)
+{
+       int setup;
+
+
+       atomic_inc(&part->references);
+       setup = (part->setup_state == XPC_P_SETUP);
+       if (!setup) {
+               xpc_part_deref(part);
+       }
+       return setup;
+}
+
+
+
+/*
+ * The following macro is to be used for the setting of the reason and
+ * reason_line fields in both the struct xpc_channel and struct xpc_partition
+ * structures.
+ */
+#define XPC_SET_REASON(_p, _reason, _line) \
+       { \
+               (_p)->reason = _reason; \
+               (_p)->reason_line = _line; \
+       }
+
+
+
+/*
+ * The following set of macros and inlines are used for the sending and
+ * receiving of IPIs (also known as IRQs). There are two flavors of IPIs,
+ * one that is associated with partition activity (SGI_XPC_ACTIVATE) and
+ * the other that is associated with channel activity (SGI_XPC_NOTIFY).
+ */
+
+static inline u64
+xpc_IPI_receive(AMO_t *amo)
+{
+       return FETCHOP_LOAD_OP(TO_AMO((u64) &amo->variable), FETCHOP_CLEAR);
+}
+
+
+static inline enum xpc_retval
+xpc_IPI_send(AMO_t *amo, u64 flag, int nasid, int phys_cpuid, int vector)
+{
+       int ret = 0;
+       unsigned long irq_flags;
+
+
+       local_irq_save(irq_flags);
+
+       FETCHOP_STORE_OP(TO_AMO((u64) &amo->variable), FETCHOP_OR, flag);
+       sn_send_IPI_phys(nasid, phys_cpuid, vector, 0);
+
+       /*
+        * We must always use the nofault function regardless of whether we
+        * are on a Shub 1.1 system or a Shub 1.2 slice 0xc processor. If we
+        * didn't, we'd never know that the other partition is down and would
+        * keep sending IPIs and AMOs to it until the heartbeat times out.
+        */
+       ret = xp_nofault_PIOR((u64 *) GLOBAL_MMR_ADDR(NASID_GET(&amo->variable),
+                               xp_nofault_PIOR_target));
+
+       local_irq_restore(irq_flags);
+
+       return ((ret == 0) ? xpcSuccess : xpcPioReadError);
+}
+
+
+/*
+ * IPIs associated with SGI_XPC_ACTIVATE IRQ.
+ */
+
+/*
+ * Flag the appropriate AMO variable and send an IPI to the specified node.
+ */
+static inline void
+xpc_activate_IRQ_send(u64 amos_page, int from_nasid, int to_nasid,
+                       int to_phys_cpuid)
+{
+       int w_index = XPC_NASID_W_INDEX(from_nasid);
+       int b_index = XPC_NASID_B_INDEX(from_nasid);
+       AMO_t *amos = (AMO_t *) __va(amos_page +
+                                       (XP_MAX_PARTITIONS * sizeof(AMO_t)));
+
+
+       (void) xpc_IPI_send(&amos[w_index], (1UL << b_index), to_nasid,
+                               to_phys_cpuid, SGI_XPC_ACTIVATE);
+}
+
+static inline void
+xpc_IPI_send_activate(struct xpc_vars *vars)
+{
+       xpc_activate_IRQ_send(vars->amos_page_pa, cnodeid_to_nasid(0),
+                               vars->act_nasid, vars->act_phys_cpuid);
+}
+
+static inline void
+xpc_IPI_send_activated(struct xpc_partition *part)
+{
+       xpc_activate_IRQ_send(part->remote_amos_page_pa, cnodeid_to_nasid(0),
+                       part->remote_act_nasid, part->remote_act_phys_cpuid);
+}
+
+static inline void
+xpc_IPI_send_reactivate(struct xpc_partition *part)
+{
+       xpc_activate_IRQ_send(xpc_vars->amos_page_pa, part->reactivate_nasid,
+                               xpc_vars->act_nasid, xpc_vars->act_phys_cpuid);
+}
+
+
+/*
+ * IPIs associated with SGI_XPC_NOTIFY IRQ.
+ */
+
+/*
+ * Send an IPI to the remote partition that is associated with the
+ * specified channel.
+ */
+#define XPC_NOTIFY_IRQ_SEND(_ch, _ipi_f, _irq_f) \
+               xpc_notify_IRQ_send(_ch, _ipi_f, #_ipi_f, _irq_f)
+
+static inline void
+xpc_notify_IRQ_send(struct xpc_channel *ch, u8 ipi_flag, char *ipi_flag_string,
+                       unsigned long *irq_flags)
+{
+       struct xpc_partition *part = &xpc_partitions[ch->partid];
+       enum xpc_retval ret;
+
+
+       if (likely(part->act_state != XPC_P_DEACTIVATING)) {
+               ret = xpc_IPI_send(part->remote_IPI_amo_va,
+                                       (u64) ipi_flag << (ch->number * 8),
+                                       part->remote_IPI_nasid,
+                                       part->remote_IPI_phys_cpuid,
+                                       SGI_XPC_NOTIFY);
+               dev_dbg(xpc_chan, "%s sent to partid=%d, channel=%d, ret=%d\n",
+                       ipi_flag_string, ch->partid, ch->number, ret);
+               if (unlikely(ret != xpcSuccess)) {
+                       if (irq_flags != NULL) {
+                               spin_unlock_irqrestore(&ch->lock, *irq_flags);
+                       }
+                       XPC_DEACTIVATE_PARTITION(part, ret);
+                       if (irq_flags != NULL) {
+                               spin_lock_irqsave(&ch->lock, *irq_flags);
+                       }
+               }
+       }
+}
+
+
+/*
+ * Make it look like the remote partition, which is associated with the
+ * specified channel, sent us an IPI. This faked IPI will be handled
+ * by xpc_dropped_IPI_check().
+ */
+#define XPC_NOTIFY_IRQ_SEND_LOCAL(_ch, _ipi_f) \
+               xpc_notify_IRQ_send_local(_ch, _ipi_f, #_ipi_f)
+
+static inline void
+xpc_notify_IRQ_send_local(struct xpc_channel *ch, u8 ipi_flag,
+                               char *ipi_flag_string)
+{
+       struct xpc_partition *part = &xpc_partitions[ch->partid];
+
+
+       FETCHOP_STORE_OP(TO_AMO((u64) &part->local_IPI_amo_va->variable),
+                       FETCHOP_OR, ((u64) ipi_flag << (ch->number * 8)));
+       dev_dbg(xpc_chan, "%s sent local from partid=%d, channel=%d\n",
+               ipi_flag_string, ch->partid, ch->number);
+}
+
+
+/*
+ * The sending and receiving of IPIs includes the setting of an AMO variable
+ * to indicate the reason the IPI was sent. The 64-bit variable is divided
+ * up into eight bytes, ordered from right to left. Byte zero pertains to
+ * channel 0, byte one to channel 1, and so on. Each byte is described by
+ * the following IPI flags.
+ */
+
+#define        XPC_IPI_CLOSEREQUEST    0x01
+#define        XPC_IPI_CLOSEREPLY      0x02
+#define        XPC_IPI_OPENREQUEST     0x04
+#define        XPC_IPI_OPENREPLY       0x08
+#define        XPC_IPI_MSGREQUEST      0x10
+
+
+/* given an AMO variable and a channel#, get its associated IPI flags */
+#define XPC_GET_IPI_FLAGS(_amo, _c)    ((u8) (((_amo) >> ((_c) * 8)) & 0xff))
+
+#define        XPC_ANY_OPENCLOSE_IPI_FLAGS_SET(_amo) ((_amo) & 0x0f0f0f0f0f0f0f0f)
+#define XPC_ANY_MSG_IPI_FLAGS_SET(_amo)       ((_amo) & 0x1010101010101010)
+
+
+static inline void
+xpc_IPI_send_closerequest(struct xpc_channel *ch, unsigned long *irq_flags)
+{
+       struct xpc_openclose_args *args = ch->local_openclose_args;
+
+
+       args->reason = ch->reason;
+
+       XPC_NOTIFY_IRQ_SEND(ch, XPC_IPI_CLOSEREQUEST, irq_flags);
+}
+
+static inline void
+xpc_IPI_send_closereply(struct xpc_channel *ch, unsigned long *irq_flags)
+{
+       XPC_NOTIFY_IRQ_SEND(ch, XPC_IPI_CLOSEREPLY, irq_flags);
+}
+
+static inline void
+xpc_IPI_send_openrequest(struct xpc_channel *ch, unsigned long *irq_flags)
+{
+       struct xpc_openclose_args *args = ch->local_openclose_args;
+
+
+       args->msg_size = ch->msg_size;
+       args->local_nentries = ch->local_nentries;
+
+       XPC_NOTIFY_IRQ_SEND(ch, XPC_IPI_OPENREQUEST, irq_flags);
+}
+
+static inline void
+xpc_IPI_send_openreply(struct xpc_channel *ch, unsigned long *irq_flags)
+{
+       struct xpc_openclose_args *args = ch->local_openclose_args;
+
+
+       args->remote_nentries = ch->remote_nentries;
+       args->local_nentries = ch->local_nentries;
+       args->local_msgqueue_pa = __pa(ch->local_msgqueue);
+
+       XPC_NOTIFY_IRQ_SEND(ch, XPC_IPI_OPENREPLY, irq_flags);
+}
+
+static inline void
+xpc_IPI_send_msgrequest(struct xpc_channel *ch)
+{
+       XPC_NOTIFY_IRQ_SEND(ch, XPC_IPI_MSGREQUEST, NULL);
+}
+
+static inline void
+xpc_IPI_send_local_msgrequest(struct xpc_channel *ch)
+{
+       XPC_NOTIFY_IRQ_SEND_LOCAL(ch, XPC_IPI_MSGREQUEST);
+}
+
+
+/*
+ * Memory for XPC's AMO variables is allocated by the MSPEC driver. These
+ * pages are located in the lowest granule. The lowest granule uses 4k pages
+ * for cached references and an alternate TLB handler to never provide a
+ * cacheable mapping for the entire region. This will prevent speculative
+ * reading of cached copies of our lines from being issued which will cause
+ * a PI FSB Protocol error to be generated by the SHUB. For XPC, we need 64
+ * (XP_MAX_PARTITIONS) AMO variables for message notification (xpc_main.c)
+ * and an additional 16 AMO variables for partition activation (xpc_hb.c).
+ */
+static inline AMO_t *
+xpc_IPI_init(partid_t partid)
+{
+       AMO_t *part_amo = xpc_vars->amos_page + partid;
+
+
+       xpc_IPI_receive(part_amo);
+       return part_amo;
+}
+
+
+
+static inline enum xpc_retval
+xpc_map_bte_errors(bte_result_t error)
+{
+       switch (error) {
+       case BTE_SUCCESS:       return xpcSuccess;
+       case BTEFAIL_DIR:       return xpcBteDirectoryError;
+       case BTEFAIL_POISON:    return xpcBtePoisonError;
+       case BTEFAIL_WERR:      return xpcBteWriteError;
+       case BTEFAIL_ACCESS:    return xpcBteAccessError;
+       case BTEFAIL_PWERR:     return xpcBtePWriteError;
+       case BTEFAIL_PRERR:     return xpcBtePReadError;
+       case BTEFAIL_TOUT:      return xpcBteTimeOutError;
+       case BTEFAIL_XTERR:     return xpcBteXtalkError;
+       case BTEFAIL_NOTAVAIL:  return xpcBteNotAvailable;
+       default:                return xpcBteUnmappedError;
+       }
+}
+
+
+
+static inline void *
+xpc_kmalloc_cacheline_aligned(size_t size, int flags, void **base)
+{
+       /* see if kmalloc will give us cachline aligned memory by default */
+       *base = kmalloc(size, flags);
+       if (*base == NULL) {
+               return NULL;
+       }
+       if ((u64) *base == L1_CACHE_ALIGN((u64) *base)) {
+               return *base;
+       }
+       kfree(*base);
+
+       /* nope, we'll have to do it ourselves */
+       *base = kmalloc(size + L1_CACHE_BYTES, flags);
+       if (*base == NULL) {
+               return NULL;
+       }
+       return (void *) L1_CACHE_ALIGN((u64) *base);
+}
+
+
+/*
+ * Check to see if there is any channel activity to/from the specified
+ * partition.
+ */
+static inline void
+xpc_check_for_channel_activity(struct xpc_partition *part)
+{
+       u64 IPI_amo;
+       unsigned long irq_flags;
+
+
+       IPI_amo = xpc_IPI_receive(part->local_IPI_amo_va);
+       if (IPI_amo == 0) {
+               return;
+       }
+
+       spin_lock_irqsave(&part->IPI_lock, irq_flags);
+       part->local_IPI_amo |= IPI_amo;
+       spin_unlock_irqrestore(&part->IPI_lock, irq_flags);
+
+       dev_dbg(xpc_chan, "received IPI from partid=%d, IPI_amo=0x%lx\n",
+               XPC_PARTID(part), IPI_amo);
+
+       xpc_wakeup_channel_mgr(part);
+}
+
+
+#endif /* _IA64_SN_KERNEL_XPC_H */
+
diff --git a/arch/ia64/sn/kernel/xpc_channel.c b/arch/ia64/sn/kernel/xpc_channel.c
new file mode 100644 (file)
index 0000000..0bf6fbc
--- /dev/null
@@ -0,0 +1,2297 @@
+/*
+ * 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-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+
+/*
+ * Cross Partition Communication (XPC) channel support.
+ *
+ *     This is the part of XPC that manages the channels and
+ *     sends/receives messages across them to/from other partitions.
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/cache.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <asm/sn/bte.h>
+#include <asm/sn/sn_sal.h>
+#include "xpc.h"
+
+
+/*
+ * Set up the initial values for the XPartition Communication channels.
+ */
+static void
+xpc_initialize_channels(struct xpc_partition *part, partid_t partid)
+{
+       int ch_number;
+       struct xpc_channel *ch;
+
+
+       for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
+               ch = &part->channels[ch_number];
+
+               ch->partid = partid;
+               ch->number = ch_number;
+               ch->flags = XPC_C_DISCONNECTED;
+
+               ch->local_GP = &part->local_GPs[ch_number];
+               ch->local_openclose_args =
+                                       &part->local_openclose_args[ch_number];
+
+               atomic_set(&ch->kthreads_assigned, 0);
+               atomic_set(&ch->kthreads_idle, 0);
+               atomic_set(&ch->kthreads_active, 0);
+
+               atomic_set(&ch->references, 0);
+               atomic_set(&ch->n_to_notify, 0);
+
+               spin_lock_init(&ch->lock);
+               sema_init(&ch->msg_to_pull_sema, 1);    /* mutex */
+
+               atomic_set(&ch->n_on_msg_allocate_wq, 0);
+               init_waitqueue_head(&ch->msg_allocate_wq);
+               init_waitqueue_head(&ch->idle_wq);
+       }
+}
+
+
+/*
+ * Setup the infrastructure necessary to support XPartition Communication
+ * between the specified remote partition and the local one.
+ */
+enum xpc_retval
+xpc_setup_infrastructure(struct xpc_partition *part)
+{
+       int ret;
+       struct timer_list *timer;
+       partid_t partid = XPC_PARTID(part);
+
+
+       /*
+        * Zero out MOST of the entry for this partition. Only the fields
+        * starting with `nchannels' will be zeroed. The preceding fields must
+        * remain `viable' across partition ups and downs, since they may be
+        * referenced during this memset() operation.
+        */
+       memset(&part->nchannels, 0, sizeof(struct xpc_partition) -
+                               offsetof(struct xpc_partition, nchannels));
+
+       /*
+        * Allocate all of the channel structures as a contiguous chunk of
+        * memory.
+        */
+       part->channels = kmalloc(sizeof(struct xpc_channel) * XPC_NCHANNELS,
+                                                               GFP_KERNEL);
+       if (part->channels == NULL) {
+               dev_err(xpc_chan, "can't get memory for channels\n");
+               return xpcNoMemory;
+       }
+       memset(part->channels, 0, sizeof(struct xpc_channel) * XPC_NCHANNELS);
+
+       part->nchannels = XPC_NCHANNELS;
+
+
+       /* allocate all the required GET/PUT values */
+
+       part->local_GPs = xpc_kmalloc_cacheline_aligned(XPC_GP_SIZE,
+                                       GFP_KERNEL, &part->local_GPs_base);
+       if (part->local_GPs == NULL) {
+               kfree(part->channels);
+               part->channels = NULL;
+               dev_err(xpc_chan, "can't get memory for local get/put "
+                       "values\n");
+               return xpcNoMemory;
+       }
+       memset(part->local_GPs, 0, XPC_GP_SIZE);
+
+       part->remote_GPs = xpc_kmalloc_cacheline_aligned(XPC_GP_SIZE,
+                                       GFP_KERNEL, &part->remote_GPs_base);
+       if (part->remote_GPs == NULL) {
+               kfree(part->channels);
+               part->channels = NULL;
+               kfree(part->local_GPs_base);
+               part->local_GPs = NULL;
+               dev_err(xpc_chan, "can't get memory for remote get/put "
+                       "values\n");
+               return xpcNoMemory;
+       }
+       memset(part->remote_GPs, 0, XPC_GP_SIZE);
+
+
+       /* allocate all the required open and close args */
+
+       part->local_openclose_args = xpc_kmalloc_cacheline_aligned(
+                                       XPC_OPENCLOSE_ARGS_SIZE, GFP_KERNEL,
+                                       &part->local_openclose_args_base);
+       if (part->local_openclose_args == NULL) {
+               kfree(part->channels);
+               part->channels = NULL;
+               kfree(part->local_GPs_base);
+               part->local_GPs = NULL;
+               kfree(part->remote_GPs_base);
+               part->remote_GPs = NULL;
+               dev_err(xpc_chan, "can't get memory for local connect args\n");
+               return xpcNoMemory;
+       }
+       memset(part->local_openclose_args, 0, XPC_OPENCLOSE_ARGS_SIZE);
+
+       part->remote_openclose_args = xpc_kmalloc_cacheline_aligned(
+                                       XPC_OPENCLOSE_ARGS_SIZE, GFP_KERNEL,
+                                       &part->remote_openclose_args_base);
+       if (part->remote_openclose_args == NULL) {
+               kfree(part->channels);
+               part->channels = NULL;
+               kfree(part->local_GPs_base);
+               part->local_GPs = NULL;
+               kfree(part->remote_GPs_base);
+               part->remote_GPs = NULL;
+               kfree(part->local_openclose_args_base);
+               part->local_openclose_args = NULL;
+               dev_err(xpc_chan, "can't get memory for remote connect args\n");
+               return xpcNoMemory;
+       }
+       memset(part->remote_openclose_args, 0, XPC_OPENCLOSE_ARGS_SIZE);
+
+
+       xpc_initialize_channels(part, partid);
+
+       atomic_set(&part->nchannels_active, 0);
+
+
+       /* local_IPI_amo were set to 0 by an earlier memset() */
+
+       /* Initialize this partitions AMO_t structure */
+       part->local_IPI_amo_va = xpc_IPI_init(partid);
+
+       spin_lock_init(&part->IPI_lock);
+
+       atomic_set(&part->channel_mgr_requests, 1);
+       init_waitqueue_head(&part->channel_mgr_wq);
+
+       sprintf(part->IPI_owner, "xpc%02d", partid);
+       ret = request_irq(SGI_XPC_NOTIFY, xpc_notify_IRQ_handler, SA_SHIRQ,
+                               part->IPI_owner, (void *) (u64) partid);
+       if (ret != 0) {
+               kfree(part->channels);
+               part->channels = NULL;
+               kfree(part->local_GPs_base);
+               part->local_GPs = NULL;
+               kfree(part->remote_GPs_base);
+               part->remote_GPs = NULL;
+               kfree(part->local_openclose_args_base);
+               part->local_openclose_args = NULL;
+               kfree(part->remote_openclose_args_base);
+               part->remote_openclose_args = NULL;
+               dev_err(xpc_chan, "can't register NOTIFY IRQ handler, "
+                       "errno=%d\n", -ret);
+               return xpcLackOfResources;
+       }
+
+       /* Setup a timer to check for dropped IPIs */
+       timer = &part->dropped_IPI_timer;
+       init_timer(timer);
+       timer->function = (void (*)(unsigned long)) xpc_dropped_IPI_check;
+       timer->data = (unsigned long) part;
+       timer->expires = jiffies + XPC_P_DROPPED_IPI_WAIT;
+       add_timer(timer);
+
+       /*
+        * With the setting of the partition setup_state to XPC_P_SETUP, we're
+        * declaring that this partition is ready to go.
+        */
+       (volatile u8) part->setup_state = XPC_P_SETUP;
+
+
+       /*
+        * Setup the per partition specific variables required by the
+        * remote partition to establish channel connections with us.
+        *
+        * The setting of the magic # indicates that these per partition
+        * specific variables are ready to be used.
+        */
+       xpc_vars_part[partid].GPs_pa = __pa(part->local_GPs);
+       xpc_vars_part[partid].openclose_args_pa =
+                                       __pa(part->local_openclose_args);
+       xpc_vars_part[partid].IPI_amo_pa = __pa(part->local_IPI_amo_va);
+       xpc_vars_part[partid].IPI_nasid = cpuid_to_nasid(smp_processor_id());
+       xpc_vars_part[partid].IPI_phys_cpuid =
+                                       cpu_physical_id(smp_processor_id());
+       xpc_vars_part[partid].nchannels = part->nchannels;
+       (volatile u64) xpc_vars_part[partid].magic = XPC_VP_MAGIC1;
+
+       return xpcSuccess;
+}
+
+
+/*
+ * Create a wrapper that hides the underlying mechanism for pulling a cacheline
+ * (or multiple cachelines) from a remote partition.
+ *
+ * src must be a cacheline aligned physical address on the remote partition.
+ * dst must be a cacheline aligned virtual address on this partition.
+ * cnt must be an cacheline sized
+ */
+static enum xpc_retval
+xpc_pull_remote_cachelines(struct xpc_partition *part, void *dst,
+                               const void *src, size_t cnt)
+{
+       bte_result_t bte_ret;
+
+
+       DBUG_ON((u64) src != L1_CACHE_ALIGN((u64) src));
+       DBUG_ON((u64) dst != L1_CACHE_ALIGN((u64) dst));
+       DBUG_ON(cnt != L1_CACHE_ALIGN(cnt));
+
+       if (part->act_state == XPC_P_DEACTIVATING) {
+               return part->reason;
+       }
+
+       bte_ret = xp_bte_copy((u64) src, (u64) ia64_tpa((u64) dst),
+                               (u64) cnt, (BTE_NORMAL | BTE_WACQUIRE), NULL);
+       if (bte_ret == BTE_SUCCESS) {
+               return xpcSuccess;
+       }
+
+       dev_dbg(xpc_chan, "xp_bte_copy() from partition %d failed, ret=%d\n",
+               XPC_PARTID(part), bte_ret);
+
+       return xpc_map_bte_errors(bte_ret);
+}
+
+
+/*
+ * Pull the remote per partititon specific variables from the specified
+ * partition.
+ */
+enum xpc_retval
+xpc_pull_remote_vars_part(struct xpc_partition *part)
+{
+       u8 buffer[L1_CACHE_BYTES * 2];
+       struct xpc_vars_part *pulled_entry_cacheline =
+                       (struct xpc_vars_part *) L1_CACHE_ALIGN((u64) buffer);
+       struct xpc_vars_part *pulled_entry;
+       u64 remote_entry_cacheline_pa, remote_entry_pa;
+       partid_t partid = XPC_PARTID(part);
+       enum xpc_retval ret;
+
+
+       /* pull the cacheline that contains the variables we're interested in */
+
+       DBUG_ON(part->remote_vars_part_pa !=
+                               L1_CACHE_ALIGN(part->remote_vars_part_pa));
+       DBUG_ON(sizeof(struct xpc_vars_part) != L1_CACHE_BYTES / 2);
+
+       remote_entry_pa = part->remote_vars_part_pa +
+                       sn_partition_id * sizeof(struct xpc_vars_part);
+
+       remote_entry_cacheline_pa = (remote_entry_pa & ~(L1_CACHE_BYTES - 1));
+
+       pulled_entry = (struct xpc_vars_part *) ((u64) pulled_entry_cacheline +
+                               (remote_entry_pa & (L1_CACHE_BYTES - 1)));
+
+       ret = xpc_pull_remote_cachelines(part, pulled_entry_cacheline,
+                                       (void *) remote_entry_cacheline_pa,
+                                       L1_CACHE_BYTES);
+       if (ret != xpcSuccess) {
+               dev_dbg(xpc_chan, "failed to pull XPC vars_part from "
+                       "partition %d, ret=%d\n", partid, ret);
+               return ret;
+       }
+
+
+       /* see if they've been set up yet */
+
+       if (pulled_entry->magic != XPC_VP_MAGIC1 &&
+                               pulled_entry->magic != XPC_VP_MAGIC2) {
+
+               if (pulled_entry->magic != 0) {
+                       dev_dbg(xpc_chan, "partition %d's XPC vars_part for "
+                               "partition %d has bad magic value (=0x%lx)\n",
+                               partid, sn_partition_id, pulled_entry->magic);
+                       return xpcBadMagic;
+               }
+
+               /* they've not been initialized yet */
+               return xpcRetry;
+       }
+
+       if (xpc_vars_part[partid].magic == XPC_VP_MAGIC1) {
+
+               /* validate the variables */
+
+               if (pulled_entry->GPs_pa == 0 ||
+                               pulled_entry->openclose_args_pa == 0 ||
+                                       pulled_entry->IPI_amo_pa == 0) {
+
+                       dev_err(xpc_chan, "partition %d's XPC vars_part for "
+                               "partition %d are not valid\n", partid,
+                               sn_partition_id);
+                       return xpcInvalidAddress;
+               }
+
+               /* the variables we imported look to be valid */
+
+               part->remote_GPs_pa = pulled_entry->GPs_pa;
+               part->remote_openclose_args_pa =
+                                       pulled_entry->openclose_args_pa;
+               part->remote_IPI_amo_va =
+                                     (AMO_t *) __va(pulled_entry->IPI_amo_pa);
+               part->remote_IPI_nasid = pulled_entry->IPI_nasid;
+               part->remote_IPI_phys_cpuid = pulled_entry->IPI_phys_cpuid;
+
+               if (part->nchannels > pulled_entry->nchannels) {
+                       part->nchannels = pulled_entry->nchannels;
+               }
+
+               /* let the other side know that we've pulled their variables */
+
+               (volatile u64) xpc_vars_part[partid].magic = XPC_VP_MAGIC2;
+       }
+
+       if (pulled_entry->magic == XPC_VP_MAGIC1) {
+               return xpcRetry;
+       }
+
+       return xpcSuccess;
+}
+
+
+/*
+ * Get the IPI flags and pull the openclose args and/or remote GPs as needed.
+ */
+static u64
+xpc_get_IPI_flags(struct xpc_partition *part)
+{
+       unsigned long irq_flags;
+       u64 IPI_amo;
+       enum xpc_retval ret;
+
+
+       /*
+        * See if there are any IPI flags to be handled.
+        */
+
+       spin_lock_irqsave(&part->IPI_lock, irq_flags);
+       if ((IPI_amo = part->local_IPI_amo) != 0) {
+               part->local_IPI_amo = 0;
+       }
+       spin_unlock_irqrestore(&part->IPI_lock, irq_flags);
+
+
+       if (XPC_ANY_OPENCLOSE_IPI_FLAGS_SET(IPI_amo)) {
+               ret = xpc_pull_remote_cachelines(part,
+                                       part->remote_openclose_args,
+                                       (void *) part->remote_openclose_args_pa,
+                                       XPC_OPENCLOSE_ARGS_SIZE);
+               if (ret != xpcSuccess) {
+                       XPC_DEACTIVATE_PARTITION(part, ret);
+
+                       dev_dbg(xpc_chan, "failed to pull openclose args from "
+                               "partition %d, ret=%d\n", XPC_PARTID(part),
+                               ret);
+
+                       /* don't bother processing IPIs anymore */
+                       IPI_amo = 0;
+               }
+       }
+
+       if (XPC_ANY_MSG_IPI_FLAGS_SET(IPI_amo)) {
+               ret = xpc_pull_remote_cachelines(part, part->remote_GPs,
+                                               (void *) part->remote_GPs_pa,
+                                               XPC_GP_SIZE);
+               if (ret != xpcSuccess) {
+                       XPC_DEACTIVATE_PARTITION(part, ret);
+
+                       dev_dbg(xpc_chan, "failed to pull GPs from partition "
+                               "%d, ret=%d\n", XPC_PARTID(part), ret);
+
+                       /* don't bother processing IPIs anymore */
+                       IPI_amo = 0;
+               }
+       }
+
+       return IPI_amo;
+}
+
+
+/*
+ * Allocate the local message queue and the notify queue.
+ */
+static enum xpc_retval
+xpc_allocate_local_msgqueue(struct xpc_channel *ch)
+{
+       unsigned long irq_flags;
+       int nentries;
+       size_t nbytes;
+
+
+       // >>> may want to check for ch->flags & XPC_C_DISCONNECTING between
+       // >>> iterations of the for-loop, bail if set?
+
+       // >>> should we impose a minumum #of entries? like 4 or 8?
+       for (nentries = ch->local_nentries; nentries > 0; nentries--) {
+
+               nbytes = nentries * ch->msg_size;
+               ch->local_msgqueue = xpc_kmalloc_cacheline_aligned(nbytes,
+                                               (GFP_KERNEL | GFP_DMA),
+                                               &ch->local_msgqueue_base);
+               if (ch->local_msgqueue == NULL) {
+                       continue;
+               }
+               memset(ch->local_msgqueue, 0, nbytes);
+
+               nbytes = nentries * sizeof(struct xpc_notify);
+               ch->notify_queue = kmalloc(nbytes, (GFP_KERNEL | GFP_DMA));
+               if (ch->notify_queue == NULL) {
+                       kfree(ch->local_msgqueue_base);
+                       ch->local_msgqueue = NULL;
+                       continue;
+               }
+               memset(ch->notify_queue, 0, nbytes);
+
+               spin_lock_irqsave(&ch->lock, irq_flags);
+               if (nentries < ch->local_nentries) {
+                       dev_dbg(xpc_chan, "nentries=%d local_nentries=%d, "
+                               "partid=%d, channel=%d\n", nentries,
+                               ch->local_nentries, ch->partid, ch->number);
+
+                       ch->local_nentries = nentries;
+               }
+               spin_unlock_irqrestore(&ch->lock, irq_flags);
+               return xpcSuccess;
+       }
+
+       dev_dbg(xpc_chan, "can't get memory for local message queue and notify "
+               "queue, partid=%d, channel=%d\n", ch->partid, ch->number);
+       return xpcNoMemory;
+}
+
+
+/*
+ * Allocate the cached remote message queue.
+ */
+static enum xpc_retval
+xpc_allocate_remote_msgqueue(struct xpc_channel *ch)
+{
+       unsigned long irq_flags;
+       int nentries;
+       size_t nbytes;
+
+
+       DBUG_ON(ch->remote_nentries <= 0);
+
+       // >>> may want to check for ch->flags & XPC_C_DISCONNECTING between
+       // >>> iterations of the for-loop, bail if set?
+
+       // >>> should we impose a minumum #of entries? like 4 or 8?
+       for (nentries = ch->remote_nentries; nentries > 0; nentries--) {
+
+               nbytes = nentries * ch->msg_size;
+               ch->remote_msgqueue = xpc_kmalloc_cacheline_aligned(nbytes,
+                                               (GFP_KERNEL | GFP_DMA),
+                                               &ch->remote_msgqueue_base);
+               if (ch->remote_msgqueue == NULL) {
+                       continue;
+               }
+               memset(ch->remote_msgqueue, 0, nbytes);
+
+               spin_lock_irqsave(&ch->lock, irq_flags);
+               if (nentries < ch->remote_nentries) {
+                       dev_dbg(xpc_chan, "nentries=%d remote_nentries=%d, "
+                               "partid=%d, channel=%d\n", nentries,
+                               ch->remote_nentries, ch->partid, ch->number);
+
+                       ch->remote_nentries = nentries;
+               }
+               spin_unlock_irqrestore(&ch->lock, irq_flags);
+               return xpcSuccess;
+       }
+
+       dev_dbg(xpc_chan, "can't get memory for cached remote message queue, "
+               "partid=%d, channel=%d\n", ch->partid, ch->number);
+       return xpcNoMemory;
+}
+
+
+/*
+ * Allocate message queues and other stuff associated with a channel.
+ *
+ * Note: Assumes all of the channel sizes are filled in.
+ */
+static enum xpc_retval
+xpc_allocate_msgqueues(struct xpc_channel *ch)
+{
+       unsigned long irq_flags;
+       int i;
+       enum xpc_retval ret;
+
+
+       DBUG_ON(ch->flags & XPC_C_SETUP);
+
+       if ((ret = xpc_allocate_local_msgqueue(ch)) != xpcSuccess) {
+               return ret;
+       }
+
+       if ((ret = xpc_allocate_remote_msgqueue(ch)) != xpcSuccess) {
+               kfree(ch->local_msgqueue_base);
+               ch->local_msgqueue = NULL;
+               kfree(ch->notify_queue);
+               ch->notify_queue = NULL;
+               return ret;
+       }
+
+       for (i = 0; i < ch->local_nentries; i++) {
+               /* use a semaphore as an event wait queue */
+               sema_init(&ch->notify_queue[i].sema, 0);
+       }
+
+       sema_init(&ch->teardown_sema, 0);       /* event wait */
+
+       spin_lock_irqsave(&ch->lock, irq_flags);
+       ch->flags |= XPC_C_SETUP;
+       spin_unlock_irqrestore(&ch->lock, irq_flags);
+
+       return xpcSuccess;
+}
+
+
+/*
+ * Process a connect message from a remote partition.
+ *
+ * Note: xpc_process_connect() is expecting to be called with the
+ * spin_lock_irqsave held and will leave it locked upon return.
+ */
+static void
+xpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags)
+{
+       enum xpc_retval ret;
+
+
+       DBUG_ON(!spin_is_locked(&ch->lock));
+
+       if (!(ch->flags & XPC_C_OPENREQUEST) ||
+                               !(ch->flags & XPC_C_ROPENREQUEST)) {
+               /* nothing more to do for now */
+               return;
+       }
+       DBUG_ON(!(ch->flags & XPC_C_CONNECTING));
+
+       if (!(ch->flags & XPC_C_SETUP)) {
+               spin_unlock_irqrestore(&ch->lock, *irq_flags);
+               ret = xpc_allocate_msgqueues(ch);
+               spin_lock_irqsave(&ch->lock, *irq_flags);
+
+               if (ret != xpcSuccess) {
+                       XPC_DISCONNECT_CHANNEL(ch, ret, irq_flags);
+               }
+               if (ch->flags & (XPC_C_CONNECTED | XPC_C_DISCONNECTING)) {
+                       return;
+               }
+
+               DBUG_ON(!(ch->flags & XPC_C_SETUP));
+               DBUG_ON(ch->local_msgqueue == NULL);
+               DBUG_ON(ch->remote_msgqueue == NULL);
+       }
+
+       if (!(ch->flags & XPC_C_OPENREPLY)) {
+               ch->flags |= XPC_C_OPENREPLY;
+               xpc_IPI_send_openreply(ch, irq_flags);
+       }
+
+       if (!(ch->flags & XPC_C_ROPENREPLY)) {
+               return;
+       }
+
+       DBUG_ON(ch->remote_msgqueue_pa == 0);
+
+       ch->flags = (XPC_C_CONNECTED | XPC_C_SETUP);    /* clear all else */
+
+       dev_info(xpc_chan, "channel %d to partition %d connected\n",
+               ch->number, ch->partid);
+
+       spin_unlock_irqrestore(&ch->lock, *irq_flags);
+       xpc_create_kthreads(ch, 1);
+       spin_lock_irqsave(&ch->lock, *irq_flags);
+}
+
+
+/*
+ * Free up message queues and other stuff that were allocated for the specified
+ * channel.
+ *
+ * Note: ch->reason and ch->reason_line are left set for debugging purposes,
+ * they're cleared when XPC_C_DISCONNECTED is cleared.
+ */
+static void
+xpc_free_msgqueues(struct xpc_channel *ch)
+{
+       DBUG_ON(!spin_is_locked(&ch->lock));
+       DBUG_ON(atomic_read(&ch->n_to_notify) != 0);
+
+       ch->remote_msgqueue_pa = 0;
+       ch->func = NULL;
+       ch->key = NULL;
+       ch->msg_size = 0;
+       ch->local_nentries = 0;
+       ch->remote_nentries = 0;
+       ch->kthreads_assigned_limit = 0;
+       ch->kthreads_idle_limit = 0;
+
+       ch->local_GP->get = 0;
+       ch->local_GP->put = 0;
+       ch->remote_GP.get = 0;
+       ch->remote_GP.put = 0;
+       ch->w_local_GP.get = 0;
+       ch->w_local_GP.put = 0;
+       ch->w_remote_GP.get = 0;
+       ch->w_remote_GP.put = 0;
+       ch->next_msg_to_pull = 0;
+
+       if (ch->flags & XPC_C_SETUP) {
+               ch->flags &= ~XPC_C_SETUP;
+
+               dev_dbg(xpc_chan, "ch->flags=0x%x, partid=%d, channel=%d\n",
+                       ch->flags, ch->partid, ch->number);
+
+               kfree(ch->local_msgqueue_base);
+               ch->local_msgqueue = NULL;
+               kfree(ch->remote_msgqueue_base);
+               ch->remote_msgqueue = NULL;
+               kfree(ch->notify_queue);
+               ch->notify_queue = NULL;
+
+               /* in case someone is waiting for the teardown to complete */
+               up(&ch->teardown_sema);
+       }
+}
+
+
+/*
+ * spin_lock_irqsave() is expected to be held on entry.
+ */
+static void
+xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags)
+{
+       struct xpc_partition *part = &xpc_partitions[ch->partid];
+       u32 ch_flags = ch->flags;
+
+
+       DBUG_ON(!spin_is_locked(&ch->lock));
+
+       if (!(ch->flags & XPC_C_DISCONNECTING)) {
+               return;
+       }
+
+       DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
+
+       /* make sure all activity has settled down first */
+
+       if (atomic_read(&ch->references) > 0) {
+               return;
+       }
+       DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0);
+
+       /* it's now safe to free the channel's message queues */
+
+       xpc_free_msgqueues(ch);
+       DBUG_ON(ch->flags & XPC_C_SETUP);
+
+       if (part->act_state != XPC_P_DEACTIVATING) {
+
+               /* as long as the other side is up do the full protocol */
+
+               if (!(ch->flags & XPC_C_RCLOSEREQUEST)) {
+                       return;
+               }
+
+               if (!(ch->flags & XPC_C_CLOSEREPLY)) {
+                       ch->flags |= XPC_C_CLOSEREPLY;
+                       xpc_IPI_send_closereply(ch, irq_flags);
+               }
+
+               if (!(ch->flags & XPC_C_RCLOSEREPLY)) {
+                       return;
+               }
+       }
+
+       /* both sides are disconnected now */
+
+       ch->flags = XPC_C_DISCONNECTED; /* clear all flags, but this one */
+
+       atomic_dec(&part->nchannels_active);
+
+       if (ch_flags & XPC_C_WASCONNECTED) {
+               dev_info(xpc_chan, "channel %d to partition %d disconnected, "
+                       "reason=%d\n", ch->number, ch->partid, ch->reason);
+       }
+}
+
+
+/*
+ * Process a change in the channel's remote connection state.
+ */
+static void
+xpc_process_openclose_IPI(struct xpc_partition *part, int ch_number,
+                               u8 IPI_flags)
+{
+       unsigned long irq_flags;
+       struct xpc_openclose_args *args =
+                               &part->remote_openclose_args[ch_number];
+       struct xpc_channel *ch = &part->channels[ch_number];
+       enum xpc_retval reason;
+
+
+
+       spin_lock_irqsave(&ch->lock, irq_flags);
+
+
+       if (IPI_flags & XPC_IPI_CLOSEREQUEST) {
+
+               dev_dbg(xpc_chan, "XPC_IPI_CLOSEREQUEST (reason=%d) received "
+                       "from partid=%d, channel=%d\n", args->reason,
+                       ch->partid, ch->number);
+
+               /*
+                * If RCLOSEREQUEST is set, we're probably waiting for
+                * RCLOSEREPLY. We should find it and a ROPENREQUEST packed
+                * with this RCLOSEQREUQEST in the IPI_flags.
+                */
+
+               if (ch->flags & XPC_C_RCLOSEREQUEST) {
+                       DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING));
+                       DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
+                       DBUG_ON(!(ch->flags & XPC_C_CLOSEREPLY));
+                       DBUG_ON(ch->flags & XPC_C_RCLOSEREPLY);
+
+                       DBUG_ON(!(IPI_flags & XPC_IPI_CLOSEREPLY));
+                       IPI_flags &= ~XPC_IPI_CLOSEREPLY;
+                       ch->flags |= XPC_C_RCLOSEREPLY;
+
+                       /* both sides have finished disconnecting */
+                       xpc_process_disconnect(ch, &irq_flags);
+               }
+
+               if (ch->flags & XPC_C_DISCONNECTED) {
+                       // >>> explain this section
+
+                       if (!(IPI_flags & XPC_IPI_OPENREQUEST)) {
+                               DBUG_ON(part->act_state !=
+                                                       XPC_P_DEACTIVATING);
+                               spin_unlock_irqrestore(&ch->lock, irq_flags);
+                               return;
+                       }
+
+                       XPC_SET_REASON(ch, 0, 0);
+                       ch->flags &= ~XPC_C_DISCONNECTED;
+
+                       atomic_inc(&part->nchannels_active);
+                       ch->flags |= (XPC_C_CONNECTING | XPC_C_ROPENREQUEST);
+               }
+
+               IPI_flags &= ~(XPC_IPI_OPENREQUEST | XPC_IPI_OPENREPLY);
+
+               /*
+                * The meaningful CLOSEREQUEST connection state fields are:
+                *      reason = reason connection is to be closed
+                */
+
+               ch->flags |= XPC_C_RCLOSEREQUEST;
+
+               if (!(ch->flags & XPC_C_DISCONNECTING)) {
+                       reason = args->reason;
+                       if (reason <= xpcSuccess || reason > xpcUnknownReason) {
+                               reason = xpcUnknownReason;
+                       } else if (reason == xpcUnregistering) {
+                               reason = xpcOtherUnregistering;
+                       }
+
+                       XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags);
+               } else {
+                       xpc_process_disconnect(ch, &irq_flags);
+               }
+       }
+
+
+       if (IPI_flags & XPC_IPI_CLOSEREPLY) {
+
+               dev_dbg(xpc_chan, "XPC_IPI_CLOSEREPLY received from partid=%d,"
+                       " channel=%d\n", ch->partid, ch->number);
+
+               if (ch->flags & XPC_C_DISCONNECTED) {
+                       DBUG_ON(part->act_state != XPC_P_DEACTIVATING);
+                       spin_unlock_irqrestore(&ch->lock, irq_flags);
+                       return;
+               }
+
+               DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
+               DBUG_ON(!(ch->flags & XPC_C_RCLOSEREQUEST));
+
+               ch->flags |= XPC_C_RCLOSEREPLY;
+
+               if (ch->flags & XPC_C_CLOSEREPLY) {
+                       /* both sides have finished disconnecting */
+                       xpc_process_disconnect(ch, &irq_flags);
+               }
+       }
+
+
+       if (IPI_flags & XPC_IPI_OPENREQUEST) {
+
+               dev_dbg(xpc_chan, "XPC_IPI_OPENREQUEST (msg_size=%d, "
+                       "local_nentries=%d) received from partid=%d, "
+                       "channel=%d\n", args->msg_size, args->local_nentries,
+                       ch->partid, ch->number);
+
+               if ((ch->flags & XPC_C_DISCONNECTING) ||
+                                       part->act_state == XPC_P_DEACTIVATING) {
+                       spin_unlock_irqrestore(&ch->lock, irq_flags);
+                       return;
+               }
+               DBUG_ON(!(ch->flags & (XPC_C_DISCONNECTED |
+                                                       XPC_C_OPENREQUEST)));
+               DBUG_ON(ch->flags & (XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |
+                                       XPC_C_OPENREPLY | XPC_C_CONNECTED));
+
+               /*
+                * The meaningful OPENREQUEST connection state fields are:
+                *      msg_size = size of channel's messages in bytes
+                *      local_nentries = remote partition's local_nentries
+                */
+               DBUG_ON(args->msg_size == 0);
+               DBUG_ON(args->local_nentries == 0);
+
+               ch->flags |= (XPC_C_ROPENREQUEST | XPC_C_CONNECTING);
+               ch->remote_nentries = args->local_nentries;
+
+
+               if (ch->flags & XPC_C_OPENREQUEST) {
+                       if (args->msg_size != ch->msg_size) {
+                               XPC_DISCONNECT_CHANNEL(ch, xpcUnequalMsgSizes,
+                                                               &irq_flags);
+                               spin_unlock_irqrestore(&ch->lock, irq_flags);
+                               return;
+                       }
+               } else {
+                       ch->msg_size = args->msg_size;
+
+                       XPC_SET_REASON(ch, 0, 0);
+                       ch->flags &= ~XPC_C_DISCONNECTED;
+
+                       atomic_inc(&part->nchannels_active);
+               }
+
+               xpc_process_connect(ch, &irq_flags);
+       }
+
+
+       if (IPI_flags & XPC_IPI_OPENREPLY) {
+
+               dev_dbg(xpc_chan, "XPC_IPI_OPENREPLY (local_msgqueue_pa=0x%lx, "
+                       "local_nentries=%d, remote_nentries=%d) received from "
+                       "partid=%d, channel=%d\n", args->local_msgqueue_pa,
+                       args->local_nentries, args->remote_nentries,
+                       ch->partid, ch->number);
+
+               if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) {
+                       spin_unlock_irqrestore(&ch->lock, irq_flags);
+                       return;
+               }
+               DBUG_ON(!(ch->flags & XPC_C_OPENREQUEST));
+               DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST));
+               DBUG_ON(ch->flags & XPC_C_CONNECTED);
+
+               /*
+                * The meaningful OPENREPLY connection state fields are:
+                *      local_msgqueue_pa = physical address of remote
+                *                          partition's local_msgqueue
+                *      local_nentries = remote partition's local_nentries
+                *      remote_nentries = remote partition's remote_nentries
+                */
+               DBUG_ON(args->local_msgqueue_pa == 0);
+               DBUG_ON(args->local_nentries == 0);
+               DBUG_ON(args->remote_nentries == 0);
+
+               ch->flags |= XPC_C_ROPENREPLY;
+               ch->remote_msgqueue_pa = args->local_msgqueue_pa;
+
+               if (args->local_nentries < ch->remote_nentries) {
+                       dev_dbg(xpc_chan, "XPC_IPI_OPENREPLY: new "
+                               "remote_nentries=%d, old remote_nentries=%d, "
+                               "partid=%d, channel=%d\n",
+                               args->local_nentries, ch->remote_nentries,
+                               ch->partid, ch->number);
+
+                       ch->remote_nentries = args->local_nentries;
+               }
+               if (args->remote_nentries < ch->local_nentries) {
+                       dev_dbg(xpc_chan, "XPC_IPI_OPENREPLY: new "
+                               "local_nentries=%d, old local_nentries=%d, "
+                               "partid=%d, channel=%d\n",
+                               args->remote_nentries, ch->local_nentries,
+                               ch->partid, ch->number);
+
+                       ch->local_nentries = args->remote_nentries;
+               }
+
+               xpc_process_connect(ch, &irq_flags);
+       }
+
+       spin_unlock_irqrestore(&ch->lock, irq_flags);
+}
+
+
+/*
+ * Attempt to establish a channel connection to a remote partition.
+ */
+static enum xpc_retval
+xpc_connect_channel(struct xpc_channel *ch)
+{
+       unsigned long irq_flags;
+       struct xpc_registration *registration = &xpc_registrations[ch->number];
+
+
+       if (down_interruptible(&registration->sema) != 0) {
+               return xpcInterrupted;
+       }
+
+       if (!XPC_CHANNEL_REGISTERED(ch->number)) {
+               up(&registration->sema);
+               return xpcUnregistered;
+       }
+
+       spin_lock_irqsave(&ch->lock, irq_flags);
+
+       DBUG_ON(ch->flags & XPC_C_CONNECTED);
+       DBUG_ON(ch->flags & XPC_C_OPENREQUEST);
+
+       if (ch->flags & XPC_C_DISCONNECTING) {
+               spin_unlock_irqrestore(&ch->lock, irq_flags);
+               up(&registration->sema);
+               return ch->reason;
+       }
+
+
+       /* add info from the channel connect registration to the channel */
+
+       ch->kthreads_assigned_limit = registration->assigned_limit;
+       ch->kthreads_idle_limit = registration->idle_limit;
+       DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0);
+       DBUG_ON(atomic_read(&ch->kthreads_idle) != 0);
+       DBUG_ON(atomic_read(&ch->kthreads_active) != 0);
+
+       ch->func = registration->func;
+       DBUG_ON(registration->func == NULL);
+       ch->key = registration->key;
+
+       ch->local_nentries = registration->nentries;
+
+       if (ch->flags & XPC_C_ROPENREQUEST) {
+               if (registration->msg_size != ch->msg_size) {
+                       /* the local and remote sides aren't the same */
+
+                       /*
+                        * Because XPC_DISCONNECT_CHANNEL() can block we're
+                        * forced to up the registration sema before we unlock
+                        * the channel lock. But that's okay here because we're
+                        * done with the part that required the registration
+                        * sema. XPC_DISCONNECT_CHANNEL() requires that the
+                        * channel lock be locked and will unlock and relock
+                        * the channel lock as needed.
+                        */
+                       up(&registration->sema);
+                       XPC_DISCONNECT_CHANNEL(ch, xpcUnequalMsgSizes,
+                                                               &irq_flags);
+                       spin_unlock_irqrestore(&ch->lock, irq_flags);
+                       return xpcUnequalMsgSizes;
+               }
+       } else {
+               ch->msg_size = registration->msg_size;
+
+               XPC_SET_REASON(ch, 0, 0);
+               ch->flags &= ~XPC_C_DISCONNECTED;
+
+               atomic_inc(&xpc_partitions[ch->partid].nchannels_active);
+       }
+
+       up(&registration->sema);
+
+
+       /* initiate the connection */
+
+       ch->flags |= (XPC_C_OPENREQUEST | XPC_C_CONNECTING);
+       xpc_IPI_send_openrequest(ch, &irq_flags);
+
+       xpc_process_connect(ch, &irq_flags);
+
+       spin_unlock_irqrestore(&ch->lock, irq_flags);
+
+       return xpcSuccess;
+}
+
+
+/*
+ * Notify those who wanted to be notified upon delivery of their message.
+ */
+static void
+xpc_notify_senders(struct xpc_channel *ch, enum xpc_retval reason, s64 put)
+{
+       struct xpc_notify *notify;
+       u8 notify_type;
+       s64 get = ch->w_remote_GP.get - 1;
+
+
+       while (++get < put && atomic_read(&ch->n_to_notify) > 0) {
+
+               notify = &ch->notify_queue[get % ch->local_nentries];
+
+               /*
+                * See if the notify entry indicates it was associated with
+                * a message who's sender wants to be notified. It is possible
+                * that it is, but someone else is doing or has done the
+                * notification.
+                */
+               notify_type = notify->type;
+               if (notify_type == 0 ||
+                               cmpxchg(&notify->type, notify_type, 0) !=
+                                                               notify_type) {
+                       continue;
+               }
+
+               DBUG_ON(notify_type != XPC_N_CALL);
+
+               atomic_dec(&ch->n_to_notify);
+
+               if (notify->func != NULL) {
+                       dev_dbg(xpc_chan, "notify->func() called, notify=0x%p, "
+                               "msg_number=%ld, partid=%d, channel=%d\n",
+                               (void *) notify, get, ch->partid, ch->number);
+
+                       notify->func(reason, ch->partid, ch->number,
+                                                               notify->key);
+
+                       dev_dbg(xpc_chan, "notify->func() returned, "
+                               "notify=0x%p, msg_number=%ld, partid=%d, "
+                               "channel=%d\n", (void *) notify, get,
+                               ch->partid, ch->number);
+               }
+       }
+}
+
+
+/*
+ * Clear some of the msg flags in the local message queue.
+ */
+static inline void
+xpc_clear_local_msgqueue_flags(struct xpc_channel *ch)
+{
+       struct xpc_msg *msg;
+       s64 get;
+
+
+       get = ch->w_remote_GP.get;
+       do {
+               msg = (struct xpc_msg *) ((u64) ch->local_msgqueue +
+                               (get % ch->local_nentries) * ch->msg_size);
+               msg->flags = 0;
+       } while (++get < (volatile s64) ch->remote_GP.get);
+}
+
+
+/*
+ * Clear some of the msg flags in the remote message queue.
+ */
+static inline void
+xpc_clear_remote_msgqueue_flags(struct xpc_channel *ch)
+{
+       struct xpc_msg *msg;
+       s64 put;
+
+
+       put = ch->w_remote_GP.put;
+       do {
+               msg = (struct xpc_msg *) ((u64) ch->remote_msgqueue +
+                               (put % ch->remote_nentries) * ch->msg_size);
+               msg->flags = 0;
+       } while (++put < (volatile s64) ch->remote_GP.put);
+}
+
+
+static void
+xpc_process_msg_IPI(struct xpc_partition *part, int ch_number)
+{
+       struct xpc_channel *ch = &part->channels[ch_number];
+       int nmsgs_sent;
+
+
+       ch->remote_GP = part->remote_GPs[ch_number];
+
+
+       /* See what, if anything, has changed for each connected channel */
+
+       xpc_msgqueue_ref(ch);
+
+       if (ch->w_remote_GP.get == ch->remote_GP.get &&
+                               ch->w_remote_GP.put == ch->remote_GP.put) {
+               /* nothing changed since GPs were last pulled */
+               xpc_msgqueue_deref(ch);
+               return;
+       }
+
+       if (!(ch->flags & XPC_C_CONNECTED)){
+               xpc_msgqueue_deref(ch);
+               return;
+       }
+
+
+       /*
+        * First check to see if messages recently sent by us have been
+        * received by the other side. (The remote GET value will have
+        * changed since we last looked at it.)
+        */
+
+       if (ch->w_remote_GP.get != ch->remote_GP.get) {
+
+               /*
+                * We need to notify any senders that want to be notified
+                * that their sent messages have been received by their
+                * intended recipients. We need to do this before updating
+                * w_remote_GP.get so that we don't allocate the same message
+                * queue entries prematurely (see xpc_allocate_msg()).
+                */
+               if (atomic_read(&ch->n_to_notify) > 0) {
+                       /*
+                        * Notify senders that messages sent have been
+                        * received and delivered by the other side.
+                        */
+                       xpc_notify_senders(ch, xpcMsgDelivered,
+                                                       ch->remote_GP.get);
+               }
+
+               /*
+                * Clear msg->flags in previously sent messages, so that
+                * they're ready for xpc_allocate_msg().
+                */
+               xpc_clear_local_msgqueue_flags(ch);
+
+               (volatile s64) ch->w_remote_GP.get = ch->remote_GP.get;
+
+               dev_dbg(xpc_chan, "w_remote_GP.get changed to %ld, partid=%d, "
+                       "channel=%d\n", ch->w_remote_GP.get, ch->partid,
+                       ch->number);
+
+               /*
+                * If anyone was waiting for message queue entries to become
+                * available, wake them up.
+                */
+               if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) {
+                       wake_up(&ch->msg_allocate_wq);
+               }
+       }
+
+
+       /*
+        * Now check for newly sent messages by the other side. (The remote
+        * PUT value will have changed since we last looked at it.)
+        */
+
+       if (ch->w_remote_GP.put != ch->remote_GP.put) {
+               /*
+                * Clear msg->flags in previously received messages, so that
+                * they're ready for xpc_get_deliverable_msg().
+                */
+               xpc_clear_remote_msgqueue_flags(ch);
+
+               (volatile s64) ch->w_remote_GP.put = ch->remote_GP.put;
+
+               dev_dbg(xpc_chan, "w_remote_GP.put changed to %ld, partid=%d, "
+                       "channel=%d\n", ch->w_remote_GP.put, ch->partid,
+                       ch->number);
+
+               nmsgs_sent = ch->w_remote_GP.put - ch->w_local_GP.get;
+               if (nmsgs_sent > 0) {
+                       dev_dbg(xpc_chan, "msgs waiting to be copied and "
+                               "delivered=%d, partid=%d, channel=%d\n",
+                               nmsgs_sent, ch->partid, ch->number);
+
+                       if (ch->flags & XPC_C_CONNECTCALLOUT) {
+                               xpc_activate_kthreads(ch, nmsgs_sent);
+                       }
+               }
+       }
+
+       xpc_msgqueue_deref(ch);
+}
+
+
+void
+xpc_process_channel_activity(struct xpc_partition *part)
+{
+       unsigned long irq_flags;
+       u64 IPI_amo, IPI_flags;
+       struct xpc_channel *ch;
+       int ch_number;
+
+
+       IPI_amo = xpc_get_IPI_flags(part);
+
+       /*
+        * Initiate channel connections for registered channels.
+        *
+        * For each connected channel that has pending messages activate idle
+        * kthreads and/or create new kthreads as needed.
+        */
+
+       for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
+               ch = &part->channels[ch_number];
+
+
+               /*
+                * Process any open or close related IPI flags, and then deal
+                * with connecting or disconnecting the channel as required.
+                */
+
+               IPI_flags = XPC_GET_IPI_FLAGS(IPI_amo, ch_number);
+
+               if (XPC_ANY_OPENCLOSE_IPI_FLAGS_SET(IPI_flags)) {
+                       xpc_process_openclose_IPI(part, ch_number, IPI_flags);
+               }
+
+
+               if (ch->flags & XPC_C_DISCONNECTING) {
+                       spin_lock_irqsave(&ch->lock, irq_flags);
+                       xpc_process_disconnect(ch, &irq_flags);
+                       spin_unlock_irqrestore(&ch->lock, irq_flags);
+                       continue;
+               }
+
+               if (part->act_state == XPC_P_DEACTIVATING) {
+                       continue;
+               }
+
+               if (!(ch->flags & XPC_C_CONNECTED)) {
+                       if (!(ch->flags & XPC_C_OPENREQUEST)) {
+                               DBUG_ON(ch->flags & XPC_C_SETUP);
+                               (void) xpc_connect_channel(ch);
+                       } else {
+                               spin_lock_irqsave(&ch->lock, irq_flags);
+                               xpc_process_connect(ch, &irq_flags);
+                               spin_unlock_irqrestore(&ch->lock, irq_flags);
+                       }
+                       continue;
+               }
+
+
+               /*
+                * Process any message related IPI flags, this may involve the
+                * activation of kthreads to deliver any pending messages sent
+                * from the other partition.
+                */
+
+               if (XPC_ANY_MSG_IPI_FLAGS_SET(IPI_flags)) {
+                       xpc_process_msg_IPI(part, ch_number);
+               }
+       }
+}
+
+
+/*
+ * XPC's heartbeat code calls this function to inform XPC that a partition has
+ * gone down.  XPC responds by tearing down the XPartition Communication
+ * infrastructure used for the just downed partition.
+ *
+ * XPC's heartbeat code will never call this function and xpc_partition_up()
+ * at the same time. Nor will it ever make multiple calls to either function
+ * at the same time.
+ */
+void
+xpc_partition_down(struct xpc_partition *part, enum xpc_retval reason)
+{
+       unsigned long irq_flags;
+       int ch_number;
+       struct xpc_channel *ch;
+
+
+       dev_dbg(xpc_chan, "deactivating partition %d, reason=%d\n",
+               XPC_PARTID(part), reason);
+
+       if (!xpc_part_ref(part)) {
+               /* infrastructure for this partition isn't currently set up */
+               return;
+       }
+
+
+       /* disconnect all channels associated with the downed partition */
+
+       for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
+               ch = &part->channels[ch_number];
+
+
+               xpc_msgqueue_ref(ch);
+               spin_lock_irqsave(&ch->lock, irq_flags);
+
+               XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags);
+
+               spin_unlock_irqrestore(&ch->lock, irq_flags);
+               xpc_msgqueue_deref(ch);
+       }
+
+       xpc_wakeup_channel_mgr(part);
+
+       xpc_part_deref(part);
+}
+
+
+/*
+ * Teardown the infrastructure necessary to support XPartition Communication
+ * between the specified remote partition and the local one.
+ */
+void
+xpc_teardown_infrastructure(struct xpc_partition *part)
+{
+       partid_t partid = XPC_PARTID(part);
+
+
+       /*
+        * We start off by making this partition inaccessible to local
+        * processes by marking it as no longer setup. Then we make it
+        * inaccessible to remote processes by clearing the XPC per partition
+        * specific variable's magic # (which indicates that these variables
+        * are no longer valid) and by ignoring all XPC notify IPIs sent to
+        * this partition.
+        */
+
+       DBUG_ON(atomic_read(&part->nchannels_active) != 0);
+       DBUG_ON(part->setup_state != XPC_P_SETUP);
+       part->setup_state = XPC_P_WTEARDOWN;
+
+       xpc_vars_part[partid].magic = 0;
+
+
+       free_irq(SGI_XPC_NOTIFY, (void *) (u64) partid);
+
+
+       /*
+        * Before proceding with the teardown we have to wait until all
+        * existing references cease.
+        */
+       wait_event(part->teardown_wq, (atomic_read(&part->references) == 0));
+
+
+       /* now we can begin tearing down the infrastructure */
+
+       part->setup_state = XPC_P_TORNDOWN;
+
+       /* in case we've still got outstanding timers registered... */
+       del_timer_sync(&part->dropped_IPI_timer);
+
+       kfree(part->remote_openclose_args_base);
+       part->remote_openclose_args = NULL;
+       kfree(part->local_openclose_args_base);
+       part->local_openclose_args = NULL;
+       kfree(part->remote_GPs_base);
+       part->remote_GPs = NULL;
+       kfree(part->local_GPs_base);
+       part->local_GPs = NULL;
+       kfree(part->channels);
+       part->channels = NULL;
+       part->local_IPI_amo_va = NULL;
+}
+
+
+/*
+ * Called by XP at the time of channel connection registration to cause
+ * XPC to establish connections to all currently active partitions.
+ */
+void
+xpc_initiate_connect(int ch_number)
+{
+       partid_t partid;
+       struct xpc_partition *part;
+       struct xpc_channel *ch;
+
+
+       DBUG_ON(ch_number < 0 || ch_number >= XPC_NCHANNELS);
+
+       for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) {
+               part = &xpc_partitions[partid];
+
+               if (xpc_part_ref(part)) {
+                       ch = &part->channels[ch_number];
+
+                       if (!(ch->flags & XPC_C_DISCONNECTING)) {
+                               DBUG_ON(ch->flags & XPC_C_OPENREQUEST);
+                               DBUG_ON(ch->flags & XPC_C_CONNECTED);
+                               DBUG_ON(ch->flags & XPC_C_SETUP);
+
+                               /*
+                                * Initiate the establishment of a connection
+                                * on the newly registered channel to the
+                                * remote partition.
+                                */
+                               xpc_wakeup_channel_mgr(part);
+                       }
+
+                       xpc_part_deref(part);
+               }
+       }
+}
+
+
+void
+xpc_connected_callout(struct xpc_channel *ch)
+{
+       unsigned long irq_flags;
+
+
+       /* let the registerer know that a connection has been established */
+
+       if (ch->func != NULL) {
+               dev_dbg(xpc_chan, "ch->func() called, reason=xpcConnected, "
+                       "partid=%d, channel=%d\n", ch->partid, ch->number);
+
+               ch->func(xpcConnected, ch->partid, ch->number,
+                               (void *) (u64) ch->local_nentries, ch->key);
+
+               dev_dbg(xpc_chan, "ch->func() returned, reason=xpcConnected, "
+                       "partid=%d, channel=%d\n", ch->partid, ch->number);
+       }
+
+       spin_lock_irqsave(&ch->lock, irq_flags);
+       ch->flags |= XPC_C_CONNECTCALLOUT;
+       spin_unlock_irqrestore(&ch->lock, irq_flags);
+}
+
+
+/*
+ * Called by XP at the time of channel connection unregistration to cause
+ * XPC to teardown all current connections for the specified channel.
+ *
+ * Before returning xpc_initiate_disconnect() will wait until all connections
+ * on the specified channel have been closed/torndown. So the caller can be
+ * assured that they will not be receiving any more callouts from XPC to the
+ * function they registered via xpc_connect().
+ *
+ * Arguments:
+ *
+ *     ch_number - channel # to unregister.
+ */
+void
+xpc_initiate_disconnect(int ch_number)
+{
+       unsigned long irq_flags;
+       partid_t partid;
+       struct xpc_partition *part;
+       struct xpc_channel *ch;
+
+
+       DBUG_ON(ch_number < 0 || ch_number >= XPC_NCHANNELS);
+
+       /* initiate the channel disconnect for every active partition */
+       for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) {
+               part = &xpc_partitions[partid];
+
+               if (xpc_part_ref(part)) {
+                       ch = &part->channels[ch_number];
+                       xpc_msgqueue_ref(ch);
+
+                       spin_lock_irqsave(&ch->lock, irq_flags);
+
+                       XPC_DISCONNECT_CHANNEL(ch, xpcUnregistering,
+                                                               &irq_flags);
+
+                       spin_unlock_irqrestore(&ch->lock, irq_flags);
+
+                       xpc_msgqueue_deref(ch);
+                       xpc_part_deref(part);
+               }
+       }
+
+       xpc_disconnect_wait(ch_number);
+}
+
+
+/*
+ * To disconnect a channel, and reflect it back to all who may be waiting.
+ *
+ * >>> An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by
+ * >>> xpc_free_msgqueues().
+ *
+ * THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN.
+ */
+void
+xpc_disconnect_channel(const int line, struct xpc_channel *ch,
+                       enum xpc_retval reason, unsigned long *irq_flags)
+{
+       u32 flags;
+
+
+       DBUG_ON(!spin_is_locked(&ch->lock));
+
+       if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) {
+               return;
+       }
+       DBUG_ON(!(ch->flags & (XPC_C_CONNECTING | XPC_C_CONNECTED)));
+
+       dev_dbg(xpc_chan, "reason=%d, line=%d, partid=%d, channel=%d\n",
+               reason, line, ch->partid, ch->number);
+
+       XPC_SET_REASON(ch, reason, line);
+
+       flags = ch->flags;
+       /* some of these may not have been set */
+       ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY |
+                       XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |
+                       XPC_C_CONNECTING | XPC_C_CONNECTED);
+
+       ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING);
+       xpc_IPI_send_closerequest(ch, irq_flags);
+
+       if (flags & XPC_C_CONNECTED) {
+               ch->flags |= XPC_C_WASCONNECTED;
+       }
+
+       if (atomic_read(&ch->kthreads_idle) > 0) {
+               /* wake all idle kthreads so they can exit */
+               wake_up_all(&ch->idle_wq);
+       }
+
+       spin_unlock_irqrestore(&ch->lock, *irq_flags);
+
+
+       /* wake those waiting to allocate an entry from the local msg queue */
+
+       if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) {
+               wake_up(&ch->msg_allocate_wq);
+       }
+
+       /* wake those waiting for notify completion */
+
+       if (atomic_read(&ch->n_to_notify) > 0) {
+               xpc_notify_senders(ch, reason, ch->w_local_GP.put);
+       }
+
+       spin_lock_irqsave(&ch->lock, *irq_flags);
+}
+
+
+void
+xpc_disconnected_callout(struct xpc_channel *ch)
+{
+       /*
+        * Let the channel's registerer know that the channel is now
+        * disconnected. We don't want to do this if the registerer was never
+        * informed of a connection being made, unless the disconnect was for
+        * abnormal reasons.
+        */
+
+       if (ch->func != NULL) {
+               dev_dbg(xpc_chan, "ch->func() called, reason=%d, partid=%d, "
+                       "channel=%d\n", ch->reason, ch->partid, ch->number);
+
+               ch->func(ch->reason, ch->partid, ch->number, NULL, ch->key);
+
+               dev_dbg(xpc_chan, "ch->func() returned, reason=%d, partid=%d, "
+                       "channel=%d\n", ch->reason, ch->partid, ch->number);
+       }
+}
+
+
+/*
+ * Wait for a message entry to become available for the specified channel,
+ * but don't wait any longer than 1 jiffy.
+ */
+static enum xpc_retval
+xpc_allocate_msg_wait(struct xpc_channel *ch)
+{
+       enum xpc_retval ret;
+
+
+       if (ch->flags & XPC_C_DISCONNECTING) {
+               DBUG_ON(ch->reason == xpcInterrupted);  // >>> Is this true?
+               return ch->reason;
+       }
+
+       atomic_inc(&ch->n_on_msg_allocate_wq);
+       ret = interruptible_sleep_on_timeout(&ch->msg_allocate_wq, 1);
+       atomic_dec(&ch->n_on_msg_allocate_wq);
+
+       if (ch->flags & XPC_C_DISCONNECTING) {
+               ret = ch->reason;
+               DBUG_ON(ch->reason == xpcInterrupted);  // >>> Is this true?
+       } else if (ret == 0) {
+               ret = xpcTimeout;
+       } else {
+               ret = xpcInterrupted;
+       }
+
+       return ret;
+}
+
+
+/*
+ * Allocate an entry for a message from the message queue associated with the
+ * specified channel.
+ */
+static enum xpc_retval
+xpc_allocate_msg(struct xpc_channel *ch, u32 flags,
+                       struct xpc_msg **address_of_msg)
+{
+       struct xpc_msg *msg;
+       enum xpc_retval ret;
+       s64 put;
+
+
+       /* this reference will be dropped in xpc_send_msg() */
+       xpc_msgqueue_ref(ch);
+
+       if (ch->flags & XPC_C_DISCONNECTING) {
+               xpc_msgqueue_deref(ch);
+               return ch->reason;
+       }
+       if (!(ch->flags & XPC_C_CONNECTED)) {
+               xpc_msgqueue_deref(ch);
+               return xpcNotConnected;
+       }
+
+
+       /*
+        * Get the next available message entry from the local message queue.
+        * If none are available, we'll make sure that we grab the latest
+        * GP values.
+        */
+       ret = xpcTimeout;
+
+       while (1) {
+
+               put = (volatile s64) ch->w_local_GP.put;
+               if (put - (volatile s64) ch->w_remote_GP.get <
+                                                       ch->local_nentries) {
+
+                       /* There are available message entries. We need to try
+                        * to secure one for ourselves. We'll do this by trying
+                        * to increment w_local_GP.put as long as someone else
+                        * doesn't beat us to it. If they do, we'll have to
+                        * try again.
+                        */
+                       if (cmpxchg(&ch->w_local_GP.put, put, put + 1) ==
+                                                                       put) {
+                               /* we got the entry referenced by put */
+                               break;
+                       }
+                       continue;       /* try again */
+               }
+
+
+               /*
+                * There aren't any available msg entries at this time.
+                *
+                * In waiting for a message entry to become available,
+                * we set a timeout in case the other side is not
+                * sending completion IPIs. This lets us fake an IPI
+                * that will cause the IPI handler to fetch the latest
+                * GP values as if an IPI was sent by the other side.
+                */
+               if (ret == xpcTimeout) {
+                       xpc_IPI_send_local_msgrequest(ch);
+               }
+
+               if (flags & XPC_NOWAIT) {
+                       xpc_msgqueue_deref(ch);
+                       return xpcNoWait;
+               }
+
+               ret = xpc_allocate_msg_wait(ch);
+               if (ret != xpcInterrupted && ret != xpcTimeout) {
+                       xpc_msgqueue_deref(ch);
+                       return ret;
+               }
+       }
+
+
+       /* get the message's address and initialize it */
+       msg = (struct xpc_msg *) ((u64) ch->local_msgqueue +
+                               (put % ch->local_nentries) * ch->msg_size);
+
+
+       DBUG_ON(msg->flags != 0);
+       msg->number = put;
+
+       dev_dbg(xpc_chan, "w_local_GP.put changed to %ld; msg=0x%p, "
+               "msg_number=%ld, partid=%d, channel=%d\n", put + 1,
+               (void *) msg, msg->number, ch->partid, ch->number);
+
+       *address_of_msg = msg;
+
+       return xpcSuccess;
+}
+
+
+/*
+ * Allocate an entry for a message from the message queue associated with the
+ * specified channel. NOTE that this routine can sleep waiting for a message
+ * entry to become available. To not sleep, pass in the XPC_NOWAIT flag.
+ *
+ * Arguments:
+ *
+ *     partid - ID of partition to which the channel is connected.
+ *     ch_number - channel #.
+ *     flags - see xpc.h for valid flags.
+ *     payload - address of the allocated payload area pointer (filled in on
+ *               return) in which the user-defined message is constructed.
+ */
+enum xpc_retval
+xpc_initiate_allocate(partid_t partid, int ch_number, u32 flags, void **payload)
+{
+       struct xpc_partition *part = &xpc_partitions[partid];
+       enum xpc_retval ret = xpcUnknownReason;
+       struct xpc_msg *msg;
+
+
+       DBUG_ON(partid <= 0 || partid >= XP_MAX_PARTITIONS);
+       DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
+
+       *payload = NULL;
+
+       if (xpc_part_ref(part)) {
+               ret = xpc_allocate_msg(&part->channels[ch_number], flags, &msg);
+               xpc_part_deref(part);
+
+               if (msg != NULL) {
+                       *payload = &msg->payload;
+               }
+       }
+
+       return ret;
+}
+
+
+/*
+ * Now we actually send the messages that are ready to be sent by advancing
+ * the local message queue's Put value and then send an IPI to the recipient
+ * partition.
+ */
+static void
+xpc_send_msgs(struct xpc_channel *ch, s64 initial_put)
+{
+       struct xpc_msg *msg;
+       s64 put = initial_put + 1;
+       int send_IPI = 0;
+
+
+       while (1) {
+
+               while (1) {
+                       if (put == (volatile s64) ch->w_local_GP.put) {
+                               break;
+                       }
+
+                       msg = (struct xpc_msg *) ((u64) ch->local_msgqueue +
+                              (put % ch->local_nentries) * ch->msg_size);
+
+                       if (!(msg->flags & XPC_M_READY)) {
+                               break;
+                       }
+
+                       put++;
+               }
+
+               if (put == initial_put) {
+                       /* nothing's changed */
+                       break;
+               }
+
+               if (cmpxchg_rel(&ch->local_GP->put, initial_put, put) !=
+                                                               initial_put) {
+                       /* someone else beat us to it */
+                       DBUG_ON((volatile s64) ch->local_GP->put < initial_put);
+                       break;
+               }
+
+               /* we just set the new value of local_GP->put */
+
+               dev_dbg(xpc_chan, "local_GP->put changed to %ld, partid=%d, "
+                       "channel=%d\n", put, ch->partid, ch->number);
+
+               send_IPI = 1;
+
+               /*
+                * We need to ensure that the message referenced by
+                * local_GP->put is not XPC_M_READY or that local_GP->put
+                * equals w_local_GP.put, so we'll go have a look.
+                */
+               initial_put = put;
+       }
+
+       if (send_IPI) {
+               xpc_IPI_send_msgrequest(ch);
+       }
+}
+
+
+/*
+ * Common code that does the actual sending of the message by advancing the
+ * local message queue's Put value and sends an IPI to the partition the
+ * message is being sent to.
+ */
+static enum xpc_retval
+xpc_send_msg(struct xpc_channel *ch, struct xpc_msg *msg, u8 notify_type,
+                       xpc_notify_func func, void *key)
+{
+       enum xpc_retval ret = xpcSuccess;
+       struct xpc_notify *notify = NULL;   // >>> to keep the compiler happy!!
+       s64 put, msg_number = msg->number;
+
+
+       DBUG_ON(notify_type == XPC_N_CALL && func == NULL);
+       DBUG_ON((((u64) msg - (u64) ch->local_msgqueue) / ch->msg_size) !=
+                                       msg_number % ch->local_nentries);
+       DBUG_ON(msg->flags & XPC_M_READY);
+
+       if (ch->flags & XPC_C_DISCONNECTING) {
+               /* drop the reference grabbed in xpc_allocate_msg() */
+               xpc_msgqueue_deref(ch);
+               return ch->reason;
+       }
+
+       if (notify_type != 0) {
+               /*
+                * Tell the remote side to send an ACK interrupt when the
+                * message has been delivered.
+                */
+               msg->flags |= XPC_M_INTERRUPT;
+
+               atomic_inc(&ch->n_to_notify);
+
+               notify = &ch->notify_queue[msg_number % ch->local_nentries];
+               notify->func = func;
+               notify->key = key;
+               (volatile u8) notify->type = notify_type;
+
+               // >>> is a mb() needed here?
+
+               if (ch->flags & XPC_C_DISCONNECTING) {
+                       /*
+                        * An error occurred between our last error check and
+                        * this one. We will try to clear the type field from
+                        * the notify entry. If we succeed then
+                        * xpc_disconnect_channel() didn't already process
+                        * the notify entry.
+                        */
+                       if (cmpxchg(&notify->type, notify_type, 0) ==
+                                                               notify_type) {
+                               atomic_dec(&ch->n_to_notify);
+                               ret = ch->reason;
+                       }
+
+                       /* drop the reference grabbed in xpc_allocate_msg() */
+                       xpc_msgqueue_deref(ch);
+                       return ret;
+               }
+       }
+
+       msg->flags |= XPC_M_READY;
+
+       /*
+        * The preceding store of msg->flags must occur before the following
+        * load of ch->local_GP->put.
+        */
+       mb();
+
+       /* see if the message is next in line to be sent, if so send it */
+
+       put = ch->local_GP->put;
+       if (put == msg_number) {
+               xpc_send_msgs(ch, put);
+       }
+
+       /* drop the reference grabbed in xpc_allocate_msg() */
+       xpc_msgqueue_deref(ch);
+       return ret;
+}
+
+
+/*
+ * Send a message previously allocated using xpc_initiate_allocate() on the
+ * specified channel connected to the specified partition.
+ *
+ * This routine will not wait for the message to be received, nor will
+ * notification be given when it does happen. Once this routine has returned
+ * the message entry allocated via xpc_initiate_allocate() is no longer
+ * accessable to the caller.
+ *
+ * This routine, although called by users, does not call xpc_part_ref() to
+ * ensure that the partition infrastructure is in place. It relies on the
+ * fact that we called xpc_msgqueue_ref() in xpc_allocate_msg().
+ *
+ * Arguments:
+ *
+ *     partid - ID of partition to which the channel is connected.
+ *     ch_number - channel # to send message on.
+ *     payload - pointer to the payload area allocated via
+ *                     xpc_initiate_allocate().
+ */
+enum xpc_retval
+xpc_initiate_send(partid_t partid, int ch_number, void *payload)
+{
+       struct xpc_partition *part = &xpc_partitions[partid];
+       struct xpc_msg *msg = XPC_MSG_ADDRESS(payload);
+       enum xpc_retval ret;
+
+
+       dev_dbg(xpc_chan, "msg=0x%p, partid=%d, channel=%d\n", (void *) msg,
+               partid, ch_number);
+
+       DBUG_ON(partid <= 0 || partid >= XP_MAX_PARTITIONS);
+       DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
+       DBUG_ON(msg == NULL);
+
+       ret = xpc_send_msg(&part->channels[ch_number], msg, 0, NULL, NULL);
+
+       return ret;
+}
+
+
+/*
+ * Send a message previously allocated using xpc_initiate_allocate on the
+ * specified channel connected to the specified partition.
+ *
+ * This routine will not wait for the message to be sent. Once this routine
+ * has returned the message entry allocated via xpc_initiate_allocate() is no
+ * longer accessable to the caller.
+ *
+ * Once the remote end of the channel has received the message, the function
+ * passed as an argument to xpc_initiate_send_notify() will be called. This
+ * allows the sender to free up or re-use any buffers referenced by the
+ * message, but does NOT mean the message has been processed at the remote
+ * end by a receiver.
+ *
+ * If this routine returns an error, the caller's function will NOT be called.
+ *
+ * This routine, although called by users, does not call xpc_part_ref() to
+ * ensure that the partition infrastructure is in place. It relies on the
+ * fact that we called xpc_msgqueue_ref() in xpc_allocate_msg().
+ *
+ * Arguments:
+ *
+ *     partid - ID of partition to which the channel is connected.
+ *     ch_number - channel # to send message on.
+ *     payload - pointer to the payload area allocated via
+ *                     xpc_initiate_allocate().
+ *     func - function to call with asynchronous notification of message
+ *               receipt. THIS FUNCTION MUST BE NON-BLOCKING.
+ *     key - user-defined key to be passed to the function when it's called.
+ */
+enum xpc_retval
+xpc_initiate_send_notify(partid_t partid, int ch_number, void *payload,
+                               xpc_notify_func func, void *key)
+{
+       struct xpc_partition *part = &xpc_partitions[partid];
+       struct xpc_msg *msg = XPC_MSG_ADDRESS(payload);
+       enum xpc_retval ret;
+
+
+       dev_dbg(xpc_chan, "msg=0x%p, partid=%d, channel=%d\n", (void *) msg,
+               partid, ch_number);
+
+       DBUG_ON(partid <= 0 || partid >= XP_MAX_PARTITIONS);
+       DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
+       DBUG_ON(msg == NULL);
+       DBUG_ON(func == NULL);
+
+       ret = xpc_send_msg(&part->channels[ch_number], msg, XPC_N_CALL,
+                                                               func, key);
+       return ret;
+}
+
+
+static struct xpc_msg *
+xpc_pull_remote_msg(struct xpc_channel *ch, s64 get)
+{
+       struct xpc_partition *part = &xpc_partitions[ch->partid];
+       struct xpc_msg *remote_msg, *msg;
+       u32 msg_index, nmsgs;
+       u64 msg_offset;
+       enum xpc_retval ret;
+
+
+       if (down_interruptible(&ch->msg_to_pull_sema) != 0) {
+               /* we were interrupted by a signal */
+               return NULL;
+       }
+
+       while (get >= ch->next_msg_to_pull) {
+
+               /* pull as many messages as are ready and able to be pulled */
+
+               msg_index = ch->next_msg_to_pull % ch->remote_nentries;
+
+               DBUG_ON(ch->next_msg_to_pull >=
+                                       (volatile s64) ch->w_remote_GP.put);
+               nmsgs =  (volatile s64) ch->w_remote_GP.put -
+                                               ch->next_msg_to_pull;
+               if (msg_index + nmsgs > ch->remote_nentries) {
+                       /* ignore the ones that wrap the msg queue for now */
+                       nmsgs = ch->remote_nentries - msg_index;
+               }
+
+               msg_offset = msg_index * ch->msg_size;
+               msg = (struct xpc_msg *) ((u64) ch->remote_msgqueue +
+                                                               msg_offset);
+               remote_msg = (struct xpc_msg *) (ch->remote_msgqueue_pa +
+                                                               msg_offset);
+
+               if ((ret = xpc_pull_remote_cachelines(part, msg, remote_msg,
+                               nmsgs * ch->msg_size)) != xpcSuccess) {
+
+                       dev_dbg(xpc_chan, "failed to pull %d msgs starting with"
+                               " msg %ld from partition %d, channel=%d, "
+                               "ret=%d\n", nmsgs, ch->next_msg_to_pull,
+                               ch->partid, ch->number, ret);
+
+                       XPC_DEACTIVATE_PARTITION(part, ret);
+
+                       up(&ch->msg_to_pull_sema);
+                       return NULL;
+               }
+
+               mb();   /* >>> this may not be needed, we're not sure */
+
+               ch->next_msg_to_pull += nmsgs;
+       }
+
+       up(&ch->msg_to_pull_sema);
+
+       /* return the message we were looking for */
+       msg_offset = (get % ch->remote_nentries) * ch->msg_size;
+       msg = (struct xpc_msg *) ((u64) ch->remote_msgqueue + msg_offset);
+
+       return msg;
+}
+
+
+/*
+ * Get a message to be delivered.
+ */
+static struct xpc_msg *
+xpc_get_deliverable_msg(struct xpc_channel *ch)
+{
+       struct xpc_msg *msg = NULL;
+       s64 get;
+
+
+       do {
+               if ((volatile u32) ch->flags & XPC_C_DISCONNECTING) {
+                       break;
+               }
+
+               get = (volatile s64) ch->w_local_GP.get;
+               if (get == (volatile s64) ch->w_remote_GP.put) {
+                       break;
+               }
+
+               /* There are messages waiting to be pulled and delivered.
+                * We need to try to secure one for ourselves. We'll do this
+                * by trying to increment w_local_GP.get and hope that no one
+                * else beats us to it. If they do, we'll we'll simply have
+                * to try again for the next one.
+                */
+
+               if (cmpxchg(&ch->w_local_GP.get, get, get + 1) == get) {
+                       /* we got the entry referenced by get */
+
+                       dev_dbg(xpc_chan, "w_local_GP.get changed to %ld, "
+                               "partid=%d, channel=%d\n", get + 1,
+                               ch->partid, ch->number);
+
+                       /* pull the message from the remote partition */
+
+                       msg = xpc_pull_remote_msg(ch, get);
+
+                       DBUG_ON(msg != NULL && msg->number != get);
+                       DBUG_ON(msg != NULL && (msg->flags & XPC_M_DONE));
+                       DBUG_ON(msg != NULL && !(msg->flags & XPC_M_READY));
+
+                       break;
+               }
+
+       } while (1);
+
+       return msg;
+}
+
+
+/*
+ * Deliver a message to its intended recipient.
+ */
+void
+xpc_deliver_msg(struct xpc_channel *ch)
+{
+       struct xpc_msg *msg;
+
+
+       if ((msg = xpc_get_deliverable_msg(ch)) != NULL) {
+
+               /*
+                * This ref is taken to protect the payload itself from being
+                * freed before the user is finished with it, which the user
+                * indicates by calling xpc_initiate_received().
+                */
+               xpc_msgqueue_ref(ch);
+
+               atomic_inc(&ch->kthreads_active);
+
+               if (ch->func != NULL) {
+                       dev_dbg(xpc_chan, "ch->func() called, msg=0x%p, "
+                               "msg_number=%ld, partid=%d, channel=%d\n",
+                               (void *) msg, msg->number, ch->partid,
+                               ch->number);
+
+                       /* deliver the message to its intended recipient */
+                       ch->func(xpcMsgReceived, ch->partid, ch->number,
+                                       &msg->payload, ch->key);
+
+                       dev_dbg(xpc_chan, "ch->func() returned, msg=0x%p, "
+                               "msg_number=%ld, partid=%d, channel=%d\n",
+                               (void *) msg, msg->number, ch->partid,
+                               ch->number);
+               }
+
+               atomic_dec(&ch->kthreads_active);
+       }
+}
+
+
+/*
+ * Now we actually acknowledge the messages that have been delivered and ack'd
+ * by advancing the cached remote message queue's Get value and if requested
+ * send an IPI to the message sender's partition.
+ */
+static void
+xpc_acknowledge_msgs(struct xpc_channel *ch, s64 initial_get, u8 msg_flags)
+{
+       struct xpc_msg *msg;
+       s64 get = initial_get + 1;
+       int send_IPI = 0;
+
+
+       while (1) {
+
+               while (1) {
+                       if (get == (volatile s64) ch->w_local_GP.get) {
+                               break;
+                       }
+
+                       msg = (struct xpc_msg *) ((u64) ch->remote_msgqueue +
+                              (get % ch->remote_nentries) * ch->msg_size);
+
+                       if (!(msg->flags & XPC_M_DONE)) {
+                               break;
+                       }
+
+                       msg_flags |= msg->flags;
+                       get++;
+               }
+
+               if (get == initial_get) {
+                       /* nothing's changed */
+                       break;
+               }
+
+               if (cmpxchg_rel(&ch->local_GP->get, initial_get, get) !=
+                                                               initial_get) {
+                       /* someone else beat us to it */
+                       DBUG_ON((volatile s64) ch->local_GP->get <=
+                                                               initial_get);
+                       break;
+               }
+
+               /* we just set the new value of local_GP->get */
+
+               dev_dbg(xpc_chan, "local_GP->get changed to %ld, partid=%d, "
+                       "channel=%d\n", get, ch->partid, ch->number);
+
+               send_IPI = (msg_flags & XPC_M_INTERRUPT);
+
+               /*
+                * We need to ensure that the message referenced by
+                * local_GP->get is not XPC_M_DONE or that local_GP->get
+                * equals w_local_GP.get, so we'll go have a look.
+                */
+               initial_get = get;
+       }
+
+       if (send_IPI) {
+               xpc_IPI_send_msgrequest(ch);
+       }
+}
+
+
+/*
+ * Acknowledge receipt of a delivered message.
+ *
+ * If a message has XPC_M_INTERRUPT set, send an interrupt to the partition
+ * that sent the message.
+ *
+ * This function, although called by users, does not call xpc_part_ref() to
+ * ensure that the partition infrastructure is in place. It relies on the
+ * fact that we called xpc_msgqueue_ref() in xpc_deliver_msg().
+ *
+ * Arguments:
+ *
+ *     partid - ID of partition to which the channel is connected.
+ *     ch_number - channel # message received on.
+ *     payload - pointer to the payload area allocated via
+ *                     xpc_initiate_allocate().
+ */
+void
+xpc_initiate_received(partid_t partid, int ch_number, void *payload)
+{
+       struct xpc_partition *part = &xpc_partitions[partid];
+       struct xpc_channel *ch;
+       struct xpc_msg *msg = XPC_MSG_ADDRESS(payload);
+       s64 get, msg_number = msg->number;
+
+
+       DBUG_ON(partid <= 0 || partid >= XP_MAX_PARTITIONS);
+       DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
+
+       ch = &part->channels[ch_number];
+
+       dev_dbg(xpc_chan, "msg=0x%p, msg_number=%ld, partid=%d, channel=%d\n",
+               (void *) msg, msg_number, ch->partid, ch->number);
+
+       DBUG_ON((((u64) msg - (u64) ch->remote_msgqueue) / ch->msg_size) !=
+                                       msg_number % ch->remote_nentries);
+       DBUG_ON(msg->flags & XPC_M_DONE);
+
+       msg->flags |= XPC_M_DONE;
+
+       /*
+        * The preceding store of msg->flags must occur before the following
+        * load of ch->local_GP->get.
+        */
+       mb();
+
+       /*
+        * See if this message is next in line to be acknowledged as having
+        * been delivered.
+        */
+       get = ch->local_GP->get;
+       if (get == msg_number) {
+               xpc_acknowledge_msgs(ch, get, msg->flags);
+       }
+
+       /* the call to xpc_msgqueue_ref() was done by xpc_deliver_msg()  */
+       xpc_msgqueue_deref(ch);
+}
+
diff --git a/arch/ia64/sn/kernel/xpc_main.c b/arch/ia64/sn/kernel/xpc_main.c
new file mode 100644 (file)
index 0000000..177ddb7
--- /dev/null
@@ -0,0 +1,1064 @@
+/*
+ * 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-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+
+/*
+ * Cross Partition Communication (XPC) support - standard version.
+ *
+ *     XPC provides a message passing capability that crosses partition
+ *     boundaries. This module is made up of two parts:
+ *
+ *         partition   This part detects the presence/absence of other
+ *                     partitions. It provides a heartbeat and monitors
+ *                     the heartbeats of other partitions.
+ *
+ *         channel     This part manages the channels and sends/receives
+ *                     messages across them to/from other partitions.
+ *
+ *     There are a couple of additional functions residing in XP, which
+ *     provide an interface to XPC for its users.
+ *
+ *
+ *     Caveats:
+ *
+ *       . We currently have no way to determine which nasid an IPI came
+ *         from. Thus, xpc_IPI_send() does a remote AMO write followed by
+ *         an IPI. The AMO indicates where data is to be pulled from, so
+ *         after the IPI arrives, the remote partition checks the AMO word.
+ *         The IPI can actually arrive before the AMO however, so other code
+ *         must periodically check for this case. Also, remote AMO operations
+ *         do not reliably time out. Thus we do a remote PIO read solely to
+ *         know whether the remote partition is down and whether we should
+ *         stop sending IPIs to it. This remote PIO read operation is set up
+ *         in a special nofault region so SAL knows to ignore (and cleanup)
+ *         any errors due to the remote AMO write, PIO read, and/or PIO
+ *         write operations.
+ *
+ *         If/when new hardware solves this IPI problem, we should abandon
+ *         the current approach.
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+#include <linux/cache.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <asm/sn/intr.h>
+#include <asm/sn/sn_sal.h>
+#include <asm/uaccess.h>
+#include "xpc.h"
+
+
+/* define two XPC debug device structures to be used with dev_dbg() et al */
+
+struct device_driver xpc_dbg_name = {
+       .name = "xpc"
+};
+
+struct device xpc_part_dbg_subname = {
+       .bus_id = {0},          /* set to "part" at xpc_init() time */
+       .driver = &xpc_dbg_name
+};
+
+struct device xpc_chan_dbg_subname = {
+       .bus_id = {0},          /* set to "chan" at xpc_init() time */
+       .driver = &xpc_dbg_name
+};
+
+struct device *xpc_part = &xpc_part_dbg_subname;
+struct device *xpc_chan = &xpc_chan_dbg_subname;
+
+
+/* systune related variables for /proc/sys directories */
+
+static int xpc_hb_min = 1;
+static int xpc_hb_max = 10;
+
+static int xpc_hb_check_min = 10;
+static int xpc_hb_check_max = 120;
+
+static ctl_table xpc_sys_xpc_hb_dir[] = {
+       {
+               1,
+               "hb_interval",
+               &xpc_hb_interval,
+               sizeof(int),
+               0644,
+               NULL,
+               &proc_dointvec_minmax,
+               &sysctl_intvec,
+               NULL,
+               &xpc_hb_min, &xpc_hb_max
+       },
+       {
+               2,
+               "hb_check_interval",
+               &xpc_hb_check_interval,
+               sizeof(int),
+               0644,
+               NULL,
+               &proc_dointvec_minmax,
+               &sysctl_intvec,
+               NULL,
+               &xpc_hb_check_min, &xpc_hb_check_max
+       },
+       {0}
+};
+static ctl_table xpc_sys_xpc_dir[] = {
+       {
+               1,
+               "hb",
+               NULL,
+               0,
+               0555,
+               xpc_sys_xpc_hb_dir
+       },
+       {0}
+};
+static ctl_table xpc_sys_dir[] = {
+       {
+               1,
+               "xpc",
+               NULL,
+               0,
+               0555,
+               xpc_sys_xpc_dir
+       },
+       {0}
+};
+static struct ctl_table_header *xpc_sysctl;
+
+
+/* #of IRQs received */
+static atomic_t xpc_act_IRQ_rcvd;
+
+/* IRQ handler notifies this wait queue on receipt of an IRQ */
+static DECLARE_WAIT_QUEUE_HEAD(xpc_act_IRQ_wq);
+
+static unsigned long xpc_hb_check_timeout;
+
+/* xpc_hb_checker thread exited notification */
+static DECLARE_MUTEX_LOCKED(xpc_hb_checker_exited);
+
+/* xpc_discovery thread exited notification */
+static DECLARE_MUTEX_LOCKED(xpc_discovery_exited);
+
+
+static struct timer_list xpc_hb_timer;
+
+
+static void xpc_kthread_waitmsgs(struct xpc_partition *, struct xpc_channel *);
+
+
+/*
+ * Notify the heartbeat check thread that an IRQ has been received.
+ */
+static irqreturn_t
+xpc_act_IRQ_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       atomic_inc(&xpc_act_IRQ_rcvd);
+       wake_up_interruptible(&xpc_act_IRQ_wq);
+       return IRQ_HANDLED;
+}
+
+
+/*
+ * Timer to produce the heartbeat.  The timer structures function is
+ * already set when this is initially called.  A tunable is used to
+ * specify when the next timeout should occur.
+ */
+static void
+xpc_hb_beater(unsigned long dummy)
+{
+       xpc_vars->heartbeat++;
+
+       if (jiffies >= xpc_hb_check_timeout) {
+               wake_up_interruptible(&xpc_act_IRQ_wq);
+       }
+
+       xpc_hb_timer.expires = jiffies + (xpc_hb_interval * HZ);
+       add_timer(&xpc_hb_timer);
+}
+
+
+/*
+ * This thread is responsible for nearly all of the partition
+ * activation/deactivation.
+ */
+static int
+xpc_hb_checker(void *ignore)
+{
+       int last_IRQ_count = 0;
+       int new_IRQ_count;
+       int force_IRQ=0;
+
+
+       /* this thread was marked active by xpc_hb_init() */
+
+       daemonize(XPC_HB_CHECK_THREAD_NAME);
+
+       set_cpus_allowed(current, cpumask_of_cpu(XPC_HB_CHECK_CPU));
+
+       xpc_hb_check_timeout = jiffies + (xpc_hb_check_interval * HZ);
+
+       while (!(volatile int) xpc_exiting) {
+
+               /* wait for IRQ or timeout */
+               (void) wait_event_interruptible(xpc_act_IRQ_wq,
+                           (last_IRQ_count < atomic_read(&xpc_act_IRQ_rcvd) ||
+                                       jiffies >= xpc_hb_check_timeout ||
+                                               (volatile int) xpc_exiting));
+
+               dev_dbg(xpc_part, "woke up with %d ticks rem; %d IRQs have "
+                       "been received\n",
+                       (int) (xpc_hb_check_timeout - jiffies),
+                       atomic_read(&xpc_act_IRQ_rcvd) - last_IRQ_count);
+
+
+               /* checking of remote heartbeats is skewed by IRQ handling */
+               if (jiffies >= xpc_hb_check_timeout) {
+                       dev_dbg(xpc_part, "checking remote heartbeats\n");
+                       xpc_check_remote_hb();
+
+                       /*
+                        * We need to periodically recheck to ensure no
+                        * IPI/AMO pairs have been missed.  That check
+                        * must always reset xpc_hb_check_timeout.
+                        */
+                       force_IRQ = 1;
+               }
+
+
+               new_IRQ_count = atomic_read(&xpc_act_IRQ_rcvd);
+               if (last_IRQ_count < new_IRQ_count || force_IRQ != 0) {
+                       force_IRQ = 0;
+
+                       dev_dbg(xpc_part, "found an IRQ to process; will be "
+                               "resetting xpc_hb_check_timeout\n");
+
+                       last_IRQ_count += xpc_identify_act_IRQ_sender();
+                       if (last_IRQ_count < new_IRQ_count) {
+                               /* retry once to help avoid missing AMO */
+                               (void) xpc_identify_act_IRQ_sender();
+                       }
+                       last_IRQ_count = new_IRQ_count;
+
+                       xpc_hb_check_timeout = jiffies +
+                                          (xpc_hb_check_interval * HZ);
+               }
+       }
+
+       dev_dbg(xpc_part, "heartbeat checker is exiting\n");
+
+
+       /* mark this thread as inactive */
+       up(&xpc_hb_checker_exited);
+       return 0;
+}
+
+
+/*
+ * This thread will attempt to discover other partitions to activate
+ * based on info provided by SAL. This new thread is short lived and
+ * will exit once discovery is complete.
+ */
+static int
+xpc_initiate_discovery(void *ignore)
+{
+       daemonize(XPC_DISCOVERY_THREAD_NAME);
+
+       xpc_discovery();
+
+       dev_dbg(xpc_part, "discovery thread is exiting\n");
+
+       /* mark this thread as inactive */
+       up(&xpc_discovery_exited);
+       return 0;
+}
+
+
+/*
+ * Establish first contact with the remote partititon. This involves pulling
+ * the XPC per partition variables from the remote partition and waiting for
+ * the remote partition to pull ours.
+ */
+static enum xpc_retval
+xpc_make_first_contact(struct xpc_partition *part)
+{
+       enum xpc_retval ret;
+
+
+       while ((ret = xpc_pull_remote_vars_part(part)) != xpcSuccess) {
+               if (ret != xpcRetry) {
+                       XPC_DEACTIVATE_PARTITION(part, ret);
+                       return ret;
+               }
+
+               dev_dbg(xpc_chan, "waiting to make first contact with "
+                       "partition %d\n", XPC_PARTID(part));
+
+               /* wait a 1/4 of a second or so */
+               set_current_state(TASK_INTERRUPTIBLE);
+               (void) schedule_timeout(0.25 * HZ);
+
+               if (part->act_state == XPC_P_DEACTIVATING) {
+                       return part->reason;
+               }
+       }
+
+       return xpc_mark_partition_active(part);
+}
+
+
+/*
+ * The first kthread assigned to a newly activated partition is the one
+ * created by XPC HB with which it calls xpc_partition_up(). XPC hangs on to
+ * that kthread until the partition is brought down, at which time that kthread
+ * returns back to XPC HB. (The return of that kthread will signify to XPC HB
+ * that XPC has dismantled all communication infrastructure for the associated
+ * partition.) This kthread becomes the channel manager for that partition.
+ *
+ * Each active partition has a channel manager, who, besides connecting and
+ * disconnecting channels, will ensure that each of the partition's connected
+ * channels has the required number of assigned kthreads to get the work done.
+ */
+static void
+xpc_channel_mgr(struct xpc_partition *part)
+{
+       while (part->act_state != XPC_P_DEACTIVATING ||
+                               atomic_read(&part->nchannels_active) > 0) {
+
+               xpc_process_channel_activity(part);
+
+
+               /*
+                * Wait until we've been requested to activate kthreads or
+                * all of the channel's message queues have been torn down or
+                * a signal is pending.
+                *
+                * The channel_mgr_requests is set to 1 after being awakened,
+                * This is done to prevent the channel mgr from making one pass
+                * through the loop for each request, since he will
+                * be servicing all the requests in one pass. The reason it's
+                * set to 1 instead of 0 is so that other kthreads will know
+                * that the channel mgr is running and won't bother trying to
+                * wake him up.
+                */
+               atomic_dec(&part->channel_mgr_requests);
+               (void) wait_event_interruptible(part->channel_mgr_wq,
+                               (atomic_read(&part->channel_mgr_requests) > 0 ||
+                               (volatile u64) part->local_IPI_amo != 0 ||
+                               ((volatile u8) part->act_state ==
+                                                       XPC_P_DEACTIVATING &&
+                               atomic_read(&part->nchannels_active) == 0)));
+               atomic_set(&part->channel_mgr_requests, 1);
+
+               // >>> Does it need to wakeup periodically as well? In case we
+               // >>> miscalculated the #of kthreads to wakeup or create?
+       }
+}
+
+
+/*
+ * When XPC HB determines that a partition has come up, it will create a new
+ * kthread and that kthread will call this function to attempt to set up the
+ * basic infrastructure used for Cross Partition Communication with the newly
+ * upped partition.
+ *
+ * The kthread that was created by XPC HB and which setup the XPC
+ * infrastructure will remain assigned to the partition until the partition
+ * goes down. At which time the kthread will teardown the XPC infrastructure
+ * and then exit.
+ *
+ * XPC HB will put the remote partition's XPC per partition specific variables
+ * physical address into xpc_partitions[partid].remote_vars_part_pa prior to
+ * calling xpc_partition_up().
+ */
+static void
+xpc_partition_up(struct xpc_partition *part)
+{
+       DBUG_ON(part->channels != NULL);
+
+       dev_dbg(xpc_chan, "activating partition %d\n", XPC_PARTID(part));
+
+       if (xpc_setup_infrastructure(part) != xpcSuccess) {
+               return;
+       }
+
+       /*
+        * The kthread that XPC HB called us with will become the
+        * channel manager for this partition. It will not return
+        * back to XPC HB until the partition's XPC infrastructure
+        * has been dismantled.
+        */
+
+       (void) xpc_part_ref(part);      /* this will always succeed */
+
+       if (xpc_make_first_contact(part) == xpcSuccess) {
+               xpc_channel_mgr(part);
+       }
+
+       xpc_part_deref(part);
+
+       xpc_teardown_infrastructure(part);
+}
+
+
+static int
+xpc_activating(void *__partid)
+{
+       partid_t partid = (u64) __partid;
+       struct xpc_partition *part = &xpc_partitions[partid];
+       unsigned long irq_flags;
+       struct sched_param param = { sched_priority: MAX_USER_RT_PRIO - 1 };
+       int ret;
+
+
+       DBUG_ON(partid <= 0 || partid >= XP_MAX_PARTITIONS);
+
+       spin_lock_irqsave(&part->act_lock, irq_flags);
+
+       if (part->act_state == XPC_P_DEACTIVATING) {
+               part->act_state = XPC_P_INACTIVE;
+               spin_unlock_irqrestore(&part->act_lock, irq_flags);
+               part->remote_rp_pa = 0;
+               return 0;
+       }
+
+       /* indicate the thread is activating */
+       DBUG_ON(part->act_state != XPC_P_ACTIVATION_REQ);
+       part->act_state = XPC_P_ACTIVATING;
+
+       XPC_SET_REASON(part, 0, 0);
+       spin_unlock_irqrestore(&part->act_lock, irq_flags);
+
+       dev_dbg(xpc_part, "bringing partition %d up\n", partid);
+
+       daemonize("xpc%02d", partid);
+
+       /*
+        * This thread needs to run at a realtime priority to prevent a
+        * significant performance degradation.
+        */
+       ret = sched_setscheduler(current, SCHED_FIFO, &param);
+       if (ret != 0) {
+               dev_warn(xpc_part, "unable to set pid %d to a realtime "
+                       "priority, ret=%d\n", current->pid, ret);
+       }
+
+       /* allow this thread and its children to run on any CPU */
+       set_cpus_allowed(current, CPU_MASK_ALL);
+
+       /*
+        * Register the remote partition's AMOs with SAL so it can handle
+        * and cleanup errors within that address range should the remote
+        * partition go down. We don't unregister this range because it is
+        * difficult to tell when outstanding writes to the remote partition
+        * are finished and thus when it is safe to unregister. This should
+        * not result in wasted space in the SAL xp_addr_region table because
+        * we should get the same page for remote_amos_page_pa after module
+        * reloads and system reboots.
+        */
+       if (sn_register_xp_addr_region(part->remote_amos_page_pa,
+                                                       PAGE_SIZE, 1) < 0) {
+               dev_warn(xpc_part, "xpc_partition_up(%d) failed to register "
+                       "xp_addr region\n", partid);
+
+               spin_lock_irqsave(&part->act_lock, irq_flags);
+               part->act_state = XPC_P_INACTIVE;
+               XPC_SET_REASON(part, xpcPhysAddrRegFailed, __LINE__);
+               spin_unlock_irqrestore(&part->act_lock, irq_flags);
+               part->remote_rp_pa = 0;
+               return 0;
+       }
+
+       XPC_ALLOW_HB(partid, xpc_vars);
+       xpc_IPI_send_activated(part);
+
+
+       /*
+        * xpc_partition_up() holds this thread and marks this partition as
+        * XPC_P_ACTIVE by calling xpc_hb_mark_active().
+        */
+       (void) xpc_partition_up(part);
+
+       xpc_mark_partition_inactive(part);
+
+       if (part->reason == xpcReactivating) {
+               /* interrupting ourselves results in activating partition */
+               xpc_IPI_send_reactivate(part);
+       }
+
+       return 0;
+}
+
+
+void
+xpc_activate_partition(struct xpc_partition *part)
+{
+       partid_t partid = XPC_PARTID(part);
+       unsigned long irq_flags;
+       pid_t pid;
+
+
+       spin_lock_irqsave(&part->act_lock, irq_flags);
+
+       pid = kernel_thread(xpc_activating, (void *) ((u64) partid), 0);
+
+       DBUG_ON(part->act_state != XPC_P_INACTIVE);
+
+       if (pid > 0) {
+               part->act_state = XPC_P_ACTIVATION_REQ;
+               XPC_SET_REASON(part, xpcCloneKThread, __LINE__);
+       } else {
+               XPC_SET_REASON(part, xpcCloneKThreadFailed, __LINE__);
+       }
+
+       spin_unlock_irqrestore(&part->act_lock, irq_flags);
+}
+
+
+/*
+ * Handle the receipt of a SGI_XPC_NOTIFY IRQ by seeing whether the specified
+ * partition actually sent it. Since SGI_XPC_NOTIFY IRQs may be shared by more
+ * than one partition, we use an AMO_t structure per partition to indicate
+ * whether a partition has sent an IPI or not.  >>> If it has, then wake up the
+ * associated kthread to handle it.
+ *
+ * All SGI_XPC_NOTIFY IRQs received by XPC are the result of IPIs sent by XPC
+ * running on other partitions.
+ *
+ * Noteworthy Arguments:
+ *
+ *     irq - Interrupt ReQuest number. NOT USED.
+ *
+ *     dev_id - partid of IPI's potential sender.
+ *
+ *     regs - processor's context before the processor entered
+ *            interrupt code. NOT USED.
+ */
+irqreturn_t
+xpc_notify_IRQ_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+       partid_t partid = (partid_t) (u64) dev_id;
+       struct xpc_partition *part = &xpc_partitions[partid];
+
+
+       DBUG_ON(partid <= 0 || partid >= XP_MAX_PARTITIONS);
+
+       if (xpc_part_ref(part)) {
+               xpc_check_for_channel_activity(part);
+
+               xpc_part_deref(part);
+       }
+       return IRQ_HANDLED;
+}
+
+
+/*
+ * Check to see if xpc_notify_IRQ_handler() dropped any IPIs on the floor
+ * because the write to their associated IPI amo completed after the IRQ/IPI
+ * was received.
+ */
+void
+xpc_dropped_IPI_check(struct xpc_partition *part)
+{
+       if (xpc_part_ref(part)) {
+               xpc_check_for_channel_activity(part);
+
+               part->dropped_IPI_timer.expires = jiffies +
+                                                       XPC_P_DROPPED_IPI_WAIT;
+               add_timer(&part->dropped_IPI_timer);
+               xpc_part_deref(part);
+       }
+}
+
+
+void
+xpc_activate_kthreads(struct xpc_channel *ch, int needed)
+{
+       int idle = atomic_read(&ch->kthreads_idle);
+       int assigned = atomic_read(&ch->kthreads_assigned);
+       int wakeup;
+
+
+       DBUG_ON(needed <= 0);
+
+       if (idle > 0) {
+               wakeup = (needed > idle) ? idle : needed;
+               needed -= wakeup;
+
+               dev_dbg(xpc_chan, "wakeup %d idle kthreads, partid=%d, "
+                       "channel=%d\n", wakeup, ch->partid, ch->number);
+
+               /* only wakeup the requested number of kthreads */
+               wake_up_nr(&ch->idle_wq, wakeup);
+       }
+
+       if (needed <= 0) {
+               return;
+       }
+
+       if (needed + assigned > ch->kthreads_assigned_limit) {
+               needed = ch->kthreads_assigned_limit - assigned;
+               // >>>should never be less than 0
+               if (needed <= 0) {
+                       return;
+               }
+       }
+
+       dev_dbg(xpc_chan, "create %d new kthreads, partid=%d, channel=%d\n",
+               needed, ch->partid, ch->number);
+
+       xpc_create_kthreads(ch, needed);
+}
+
+
+/*
+ * This function is where XPC's kthreads wait for messages to deliver.
+ */
+static void
+xpc_kthread_waitmsgs(struct xpc_partition *part, struct xpc_channel *ch)
+{
+       do {
+               /* deliver messages to their intended recipients */
+
+               while ((volatile s64) ch->w_local_GP.get <
+                               (volatile s64) ch->w_remote_GP.put &&
+                                       !((volatile u32) ch->flags &
+                                               XPC_C_DISCONNECTING)) {
+                       xpc_deliver_msg(ch);
+               }
+
+               if (atomic_inc_return(&ch->kthreads_idle) >
+                                               ch->kthreads_idle_limit) {
+                       /* too many idle kthreads on this channel */
+                       atomic_dec(&ch->kthreads_idle);
+                       break;
+               }
+
+               dev_dbg(xpc_chan, "idle kthread calling "
+                       "wait_event_interruptible_exclusive()\n");
+
+               (void) wait_event_interruptible_exclusive(ch->idle_wq,
+                               ((volatile s64) ch->w_local_GP.get <
+                                       (volatile s64) ch->w_remote_GP.put ||
+                               ((volatile u32) ch->flags &
+                                               XPC_C_DISCONNECTING)));
+
+               atomic_dec(&ch->kthreads_idle);
+
+       } while (!((volatile u32) ch->flags & XPC_C_DISCONNECTING));
+}
+
+
+static int
+xpc_daemonize_kthread(void *args)
+{
+       partid_t partid = XPC_UNPACK_ARG1(args);
+       u16 ch_number = XPC_UNPACK_ARG2(args);
+       struct xpc_partition *part = &xpc_partitions[partid];
+       struct xpc_channel *ch;
+       int n_needed;
+
+
+       daemonize("xpc%02dc%d", partid, ch_number);
+
+       dev_dbg(xpc_chan, "kthread starting, partid=%d, channel=%d\n",
+               partid, ch_number);
+
+       ch = &part->channels[ch_number];
+
+       if (!(ch->flags & XPC_C_DISCONNECTING)) {
+               DBUG_ON(!(ch->flags & XPC_C_CONNECTED));
+
+               /* let registerer know that connection has been established */
+
+               if (atomic_read(&ch->kthreads_assigned) == 1) {
+                       xpc_connected_callout(ch);
+
+                       /*
+                        * It is possible that while the callout was being
+                        * made that the remote partition sent some messages.
+                        * If that is the case, we may need to activate
+                        * additional kthreads to help deliver them. We only
+                        * need one less than total #of messages to deliver.
+                        */
+                       n_needed = ch->w_remote_GP.put - ch->w_local_GP.get - 1;
+                       if (n_needed > 0 &&
+                                       !(ch->flags & XPC_C_DISCONNECTING)) {
+                               xpc_activate_kthreads(ch, n_needed);
+                       }
+               }
+
+               xpc_kthread_waitmsgs(part, ch);
+       }
+
+       if (atomic_dec_return(&ch->kthreads_assigned) == 0 &&
+                       ((ch->flags & XPC_C_CONNECTCALLOUT) ||
+                               (ch->reason != xpcUnregistering &&
+                                       ch->reason != xpcOtherUnregistering))) {
+               xpc_disconnected_callout(ch);
+       }
+
+
+       xpc_msgqueue_deref(ch);
+
+       dev_dbg(xpc_chan, "kthread exiting, partid=%d, channel=%d\n",
+               partid, ch_number);
+
+       xpc_part_deref(part);
+       return 0;
+}
+
+
+/*
+ * For each partition that XPC has established communications with, there is
+ * a minimum of one kernel thread assigned to perform any operation that
+ * may potentially sleep or block (basically the callouts to the asynchronous
+ * functions registered via xpc_connect()).
+ *
+ * Additional kthreads are created and destroyed by XPC as the workload
+ * demands.
+ *
+ * A kthread is assigned to one of the active channels that exists for a given
+ * partition.
+ */
+void
+xpc_create_kthreads(struct xpc_channel *ch, int needed)
+{
+       unsigned long irq_flags;
+       pid_t pid;
+       u64 args = XPC_PACK_ARGS(ch->partid, ch->number);
+
+
+       while (needed-- > 0) {
+               pid = kernel_thread(xpc_daemonize_kthread, (void *) args, 0);
+               if (pid < 0) {
+                       /* the fork failed */
+
+                       if (atomic_read(&ch->kthreads_assigned) <
+                                               ch->kthreads_idle_limit) {
+                               /*
+                                * Flag this as an error only if we have an
+                                * insufficient #of kthreads for the channel
+                                * to function.
+                                *
+                                * No xpc_msgqueue_ref() is needed here since
+                                * the channel mgr is doing this.
+                                */
+                               spin_lock_irqsave(&ch->lock, irq_flags);
+                               XPC_DISCONNECT_CHANNEL(ch, xpcLackOfResources,
+                                                               &irq_flags);
+                               spin_unlock_irqrestore(&ch->lock, irq_flags);
+                       }
+                       break;
+               }
+
+               /*
+                * The following is done on behalf of the newly created
+                * kthread. That kthread is responsible for doing the
+                * counterpart to the following before it exits.
+                */
+               (void) xpc_part_ref(&xpc_partitions[ch->partid]);
+               xpc_msgqueue_ref(ch);
+               atomic_inc(&ch->kthreads_assigned);
+               ch->kthreads_created++; // >>> temporary debug only!!!
+       }
+}
+
+
+void
+xpc_disconnect_wait(int ch_number)
+{
+       partid_t partid;
+       struct xpc_partition *part;
+       struct xpc_channel *ch;
+
+
+       /* now wait for all callouts to the caller's function to cease */
+       for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) {
+               part = &xpc_partitions[partid];
+
+               if (xpc_part_ref(part)) {
+                       ch = &part->channels[ch_number];
+
+// >>> how do we keep from falling into the window between our check and going
+// >>> down and coming back up where sema is re-inited?
+                       if (ch->flags & XPC_C_SETUP) {
+                               (void) down(&ch->teardown_sema);
+                       }
+
+                       xpc_part_deref(part);
+               }
+       }
+}
+
+
+static void
+xpc_do_exit(void)
+{
+       partid_t partid;
+       int active_part_count;
+       struct xpc_partition *part;
+
+
+       /* now it's time to eliminate our heartbeat */
+       del_timer_sync(&xpc_hb_timer);
+       xpc_vars->heartbeating_to_mask = 0;
+
+       /* indicate to others that our reserved page is uninitialized */
+       xpc_rsvd_page->vars_pa = 0;
+
+       /*
+        * Ignore all incoming interrupts. Without interupts the heartbeat
+        * checker won't activate any new partitions that may come up.
+        */
+       free_irq(SGI_XPC_ACTIVATE, NULL);
+
+       /*
+        * Cause the heartbeat checker and the discovery threads to exit.
+        * We don't want them attempting to activate new partitions as we
+        * try to deactivate the existing ones.
+        */
+       xpc_exiting = 1;
+       wake_up_interruptible(&xpc_act_IRQ_wq);
+
+       /* wait for the heartbeat checker thread to mark itself inactive */
+       down(&xpc_hb_checker_exited);
+
+       /* wait for the discovery thread to mark itself inactive */
+       down(&xpc_discovery_exited);
+
+
+       set_current_state(TASK_INTERRUPTIBLE);
+       schedule_timeout(0.3 * HZ);
+       set_current_state(TASK_RUNNING);
+
+
+       /* wait for all partitions to become inactive */
+
+       do {
+               active_part_count = 0;
+
+               for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) {
+                       part = &xpc_partitions[partid];
+                       if (part->act_state != XPC_P_INACTIVE) {
+                               active_part_count++;
+
+                               XPC_DEACTIVATE_PARTITION(part, xpcUnloading);
+                       }
+               }
+
+               if (active_part_count) {
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       schedule_timeout(0.3 * HZ);
+                       set_current_state(TASK_RUNNING);
+               }
+
+       } while (active_part_count > 0);
+
+
+       /* close down protections for IPI operations */
+       xpc_restrict_IPI_ops();
+
+
+       /* clear the interface to XPC's functions */
+       xpc_clear_interface();
+
+       if (xpc_sysctl) {
+               unregister_sysctl_table(xpc_sysctl);
+       }
+}
+
+
+int __init
+xpc_init(void)
+{
+       int ret;
+       partid_t partid;
+       struct xpc_partition *part;
+       pid_t pid;
+
+
+       /*
+        * xpc_remote_copy_buffer is used as a temporary buffer for bte_copy'ng
+        * both a partition's reserved page and its XPC variables. Its size was
+        * based on the size of a reserved page. So we need to ensure that the
+        * XPC variables will fit as well.
+        */
+       if (XPC_VARS_ALIGNED_SIZE > XPC_RSVD_PAGE_ALIGNED_SIZE) {
+               dev_err(xpc_part, "xpc_remote_copy_buffer is not big enough\n");
+               return -EPERM;
+       }
+       DBUG_ON((u64) xpc_remote_copy_buffer !=
+                               L1_CACHE_ALIGN((u64) xpc_remote_copy_buffer));
+
+       snprintf(xpc_part->bus_id, BUS_ID_SIZE, "part");
+       snprintf(xpc_chan->bus_id, BUS_ID_SIZE, "chan");
+
+       xpc_sysctl = register_sysctl_table(xpc_sys_dir, 1);
+
+       /*
+        * The first few fields of each entry of xpc_partitions[] need to
+        * be initialized now so that calls to xpc_connect() and
+        * xpc_disconnect() can be made prior to the activation of any remote
+        * partition. NOTE THAT NONE OF THE OTHER FIELDS BELONGING TO THESE
+        * ENTRIES ARE MEANINGFUL UNTIL AFTER AN ENTRY'S CORRESPONDING
+        * PARTITION HAS BEEN ACTIVATED.
+        */
+       for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) {
+               part = &xpc_partitions[partid];
+
+               DBUG_ON((u64) part != L1_CACHE_ALIGN((u64) part));
+
+               part->act_IRQ_rcvd = 0;
+               spin_lock_init(&part->act_lock);
+               part->act_state = XPC_P_INACTIVE;
+               XPC_SET_REASON(part, 0, 0);
+               part->setup_state = XPC_P_UNSET;
+               init_waitqueue_head(&part->teardown_wq);
+               atomic_set(&part->references, 0);
+       }
+
+       /*
+        * Open up protections for IPI operations (and AMO operations on
+        * Shub 1.1 systems).
+        */
+       xpc_allow_IPI_ops();
+
+       /*
+        * Interrupts being processed will increment this atomic variable and
+        * awaken the heartbeat thread which will process the interrupts.
+        */
+       atomic_set(&xpc_act_IRQ_rcvd, 0);
+
+       /*
+        * This is safe to do before the xpc_hb_checker thread has started
+        * because the handler releases a wait queue.  If an interrupt is
+        * received before the thread is waiting, it will not go to sleep,
+        * but rather immediately process the interrupt.
+        */
+       ret = request_irq(SGI_XPC_ACTIVATE, xpc_act_IRQ_handler, 0,
+                                                       "xpc hb", NULL);
+       if (ret != 0) {
+               dev_err(xpc_part, "can't register ACTIVATE IRQ handler, "
+                       "errno=%d\n", -ret);
+
+               xpc_restrict_IPI_ops();
+
+               if (xpc_sysctl) {
+                       unregister_sysctl_table(xpc_sysctl);
+               }
+               return -EBUSY;
+       }
+
+       /*
+        * Fill the partition reserved page with the information needed by
+        * other partitions to discover we are alive and establish initial
+        * communications.
+        */
+       xpc_rsvd_page = xpc_rsvd_page_init();
+       if (xpc_rsvd_page == NULL) {
+               dev_err(xpc_part, "could not setup our reserved page\n");
+
+               free_irq(SGI_XPC_ACTIVATE, NULL);
+               xpc_restrict_IPI_ops();
+
+               if (xpc_sysctl) {
+                       unregister_sysctl_table(xpc_sysctl);
+               }
+               return -EBUSY;
+       }
+
+
+       /*
+        * Set the beating to other partitions into motion.  This is
+        * the last requirement for other partitions' discovery to
+        * initiate communications with us.
+        */
+       init_timer(&xpc_hb_timer);
+       xpc_hb_timer.function = xpc_hb_beater;
+       xpc_hb_beater(0);
+
+
+       /*
+        * The real work-horse behind xpc.  This processes incoming
+        * interrupts and monitors remote heartbeats.
+        */
+       pid = kernel_thread(xpc_hb_checker, NULL, 0);
+       if (pid < 0) {
+               dev_err(xpc_part, "failed while forking hb check thread\n");
+
+               /* indicate to others that our reserved page is uninitialized */
+               xpc_rsvd_page->vars_pa = 0;
+
+               del_timer_sync(&xpc_hb_timer);
+               free_irq(SGI_XPC_ACTIVATE, NULL);
+               xpc_restrict_IPI_ops();
+
+               if (xpc_sysctl) {
+                       unregister_sysctl_table(xpc_sysctl);
+               }
+               return -EBUSY;
+       }
+
+
+       /*
+        * Startup a thread that will attempt to discover other partitions to
+        * activate based on info provided by SAL. This new thread is short
+        * lived and will exit once discovery is complete.
+        */
+       pid = kernel_thread(xpc_initiate_discovery, NULL, 0);
+       if (pid < 0) {
+               dev_err(xpc_part, "failed while forking discovery thread\n");
+
+               /* mark this new thread as a non-starter */
+               up(&xpc_discovery_exited);
+
+               xpc_do_exit();
+               return -EBUSY;
+       }
+
+
+       /* set the interface to point at XPC's functions */
+       xpc_set_interface(xpc_initiate_connect, xpc_initiate_disconnect,
+                         xpc_initiate_allocate, xpc_initiate_send,
+                         xpc_initiate_send_notify, xpc_initiate_received,
+                         xpc_initiate_partid_to_nasids);
+
+       return 0;
+}
+module_init(xpc_init);
+
+
+void __exit
+xpc_exit(void)
+{
+       xpc_do_exit();
+}
+module_exit(xpc_exit);
+
+
+MODULE_AUTHOR("Silicon Graphics, Inc.");
+MODULE_DESCRIPTION("Cross Partition Communication (XPC) support");
+MODULE_LICENSE("GPL");
+
+module_param(xpc_hb_interval, int, 0);
+MODULE_PARM_DESC(xpc_hb_interval, "Number of seconds between "
+               "heartbeat increments.");
+
+module_param(xpc_hb_check_interval, int, 0);
+MODULE_PARM_DESC(xpc_hb_check_interval, "Number of seconds between "
+               "heartbeat checks.");
+
diff --git a/arch/ia64/sn/kernel/xpc_partition.c b/arch/ia64/sn/kernel/xpc_partition.c
new file mode 100644 (file)
index 0000000..2c3c4a8
--- /dev/null
@@ -0,0 +1,984 @@
+/*
+ * 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-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+
+/*
+ * Cross Partition Communication (XPC) partition support.
+ *
+ *     This is the part of XPC that detects the presence/absence of
+ *     other partitions. It provides a heartbeat and monitors the
+ *     heartbeats of other partitions.
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/sysctl.h>
+#include <linux/cache.h>
+#include <linux/mmzone.h>
+#include <linux/nodemask.h>
+#include <asm/sn/bte.h>
+#include <asm/sn/intr.h>
+#include <asm/sn/sn_sal.h>
+#include <asm/sn/nodepda.h>
+#include <asm/sn/addrs.h>
+#include "xpc.h"
+
+
+/* XPC is exiting flag */
+int xpc_exiting;
+
+
+/* SH_IPI_ACCESS shub register value on startup */
+static u64 xpc_sh1_IPI_access;
+static u64 xpc_sh2_IPI_access0;
+static u64 xpc_sh2_IPI_access1;
+static u64 xpc_sh2_IPI_access2;
+static u64 xpc_sh2_IPI_access3;
+
+
+/* original protection values for each node */
+u64 xpc_prot_vec[MAX_COMPACT_NODES];
+
+
+/* this partition's reserved page */
+struct xpc_rsvd_page *xpc_rsvd_page;
+
+/* this partition's XPC variables (within the reserved page) */
+struct xpc_vars *xpc_vars;
+struct xpc_vars_part *xpc_vars_part;
+
+
+/*
+ * For performance reasons, each entry of xpc_partitions[] is cacheline
+ * aligned. And xpc_partitions[] is padded with an additional entry at the
+ * end so that the last legitimate entry doesn't share its cacheline with
+ * another variable.
+ */
+struct xpc_partition xpc_partitions[XP_MAX_PARTITIONS + 1];
+
+
+/*
+ * Generic buffer used to store a local copy of the remote partitions
+ * reserved page or XPC variables.
+ *
+ * xpc_discovery runs only once and is a seperate thread that is
+ * very likely going to be processing in parallel with receiving
+ * interrupts.
+ */
+char ____cacheline_aligned
+               xpc_remote_copy_buffer[XPC_RSVD_PAGE_ALIGNED_SIZE];
+
+
+/* systune related variables */
+int xpc_hb_interval = XPC_HB_DEFAULT_INTERVAL;
+int xpc_hb_check_interval = XPC_HB_CHECK_DEFAULT_TIMEOUT;
+
+
+/*
+ * Given a nasid, get the physical address of the  partition's reserved page
+ * for that nasid. This function returns 0 on any error.
+ */
+static u64
+xpc_get_rsvd_page_pa(int nasid, u64 buf, u64 buf_size)
+{
+       bte_result_t bte_res;
+       s64 status;
+       u64 cookie = 0;
+       u64 rp_pa = nasid;      /* seed with nasid */
+       u64 len = 0;
+
+
+       while (1) {
+
+               status = sn_partition_reserved_page_pa(buf, &cookie, &rp_pa,
+                                                               &len);
+
+               dev_dbg(xpc_part, "SAL returned with status=%li, cookie="
+                       "0x%016lx, address=0x%016lx, len=0x%016lx\n",
+                       status, cookie, rp_pa, len);
+
+               if (status != SALRET_MORE_PASSES) {
+                       break;
+               }
+
+               if (len > buf_size) {
+                       dev_err(xpc_part, "len (=0x%016lx) > buf_size\n", len);
+                       status = SALRET_ERROR;
+                       break;
+               }
+
+               bte_res = xp_bte_copy(rp_pa, ia64_tpa(buf), buf_size,
+                                       (BTE_NOTIFY | BTE_WACQUIRE), NULL);
+               if (bte_res != BTE_SUCCESS) {
+                       dev_dbg(xpc_part, "xp_bte_copy failed %i\n", bte_res);
+                       status = SALRET_ERROR;
+                       break;
+               }
+       }
+
+       if (status != SALRET_OK) {
+               rp_pa = 0;
+       }
+       dev_dbg(xpc_part, "reserved page at phys address 0x%016lx\n", rp_pa);
+       return rp_pa;
+}
+
+
+/*
+ * Fill the partition reserved page with the information needed by
+ * other partitions to discover we are alive and establish initial
+ * communications.
+ */
+struct xpc_rsvd_page *
+xpc_rsvd_page_init(void)
+{
+       struct xpc_rsvd_page *rp;
+       AMO_t *amos_page;
+       u64 rp_pa, next_cl, nasid_array = 0;
+       int i, ret;
+
+
+       /* get the local reserved page's address */
+
+       rp_pa = xpc_get_rsvd_page_pa(cnodeid_to_nasid(0),
+                                       (u64) xpc_remote_copy_buffer,
+                                               XPC_RSVD_PAGE_ALIGNED_SIZE);
+       if (rp_pa == 0) {
+               dev_err(xpc_part, "SAL failed to locate the reserved page\n");
+               return NULL;
+       }
+       rp = (struct xpc_rsvd_page *) __va(rp_pa);
+
+       if (rp->partid != sn_partition_id) {
+               dev_err(xpc_part, "the reserved page's partid of %d should be "
+                       "%d\n", rp->partid, sn_partition_id);
+               return NULL;
+       }
+
+       rp->version = XPC_RP_VERSION;
+
+       /*
+        * Place the XPC variables on the cache line following the
+        * reserved page structure.
+        */
+       next_cl = (u64) rp + XPC_RSVD_PAGE_ALIGNED_SIZE;
+       xpc_vars = (struct xpc_vars *) next_cl;
+
+       /*
+        * Before clearing xpc_vars, see if a page of AMOs had been previously
+        * allocated. If not we'll need to allocate one and set permissions
+        * so that cross-partition AMOs are allowed.
+        *
+        * The allocated AMO page needs MCA reporting to remain disabled after
+        * XPC has unloaded.  To make this work, we keep a copy of the pointer
+        * to this page (i.e., amos_page) in the struct xpc_vars structure,
+        * which is pointed to by the reserved page, and re-use that saved copy
+        * on subsequent loads of XPC. This AMO page is never freed, and its
+        * memory protections are never restricted.
+        */
+       if ((amos_page = xpc_vars->amos_page) == NULL) {
+               amos_page = (AMO_t *) mspec_kalloc_page(0);
+               if (amos_page == NULL) {
+                       dev_err(xpc_part, "can't allocate page of AMOs\n");
+                       return NULL;
+               }
+
+               /*
+                * Open up AMO-R/W to cpu.  This is done for Shub 1.1 systems
+                * when xpc_allow_IPI_ops() is called via xpc_hb_init().
+                */
+               if (!enable_shub_wars_1_1()) {
+                       ret = sn_change_memprotect(ia64_tpa((u64) amos_page),
+                                       PAGE_SIZE, SN_MEMPROT_ACCESS_CLASS_1,
+                                       &nasid_array);
+                       if (ret != 0) {
+                               dev_err(xpc_part, "can't change memory "
+                                       "protections\n");
+                               mspec_kfree_page((unsigned long) amos_page);
+                               return NULL;
+                       }
+               }
+       } else if (!IS_AMO_ADDRESS((u64) amos_page)) {
+               /*
+                * EFI's XPBOOT can also set amos_page in the reserved page,
+                * but it happens to leave it as an uncached physical address
+                * and we need it to be an uncached virtual, so we'll have to
+                * convert it.
+                */
+               if (!IS_AMO_PHYS_ADDRESS((u64) amos_page)) {
+                       dev_err(xpc_part, "previously used amos_page address "
+                               "is bad = 0x%p\n", (void *) amos_page);
+                       return NULL;
+               }
+               amos_page = (AMO_t *) TO_AMO((u64) amos_page);
+       }
+
+       memset(xpc_vars, 0, sizeof(struct xpc_vars));
+
+       /*
+        * Place the XPC per partition specific variables on the cache line
+        * following the XPC variables structure.
+        */
+       next_cl += XPC_VARS_ALIGNED_SIZE;
+       memset((u64 *) next_cl, 0, sizeof(struct xpc_vars_part) *
+                                                       XP_MAX_PARTITIONS);
+       xpc_vars_part = (struct xpc_vars_part *) next_cl;
+       xpc_vars->vars_part_pa = __pa(next_cl);
+
+       xpc_vars->version = XPC_V_VERSION;
+       xpc_vars->act_nasid = cpuid_to_nasid(0);
+       xpc_vars->act_phys_cpuid = cpu_physical_id(0);
+       xpc_vars->amos_page = amos_page;  /* save for next load of XPC */
+
+
+       /*
+        * Initialize the activation related AMO variables.
+        */
+       xpc_vars->act_amos = xpc_IPI_init(XP_MAX_PARTITIONS);
+       for (i = 1; i < XP_NASID_MASK_WORDS; i++) {
+               xpc_IPI_init(i + XP_MAX_PARTITIONS);
+       }
+       /* export AMO page's physical address to other partitions */
+       xpc_vars->amos_page_pa = ia64_tpa((u64) xpc_vars->amos_page);
+
+       /*
+        * This signifies to the remote partition that our reserved
+        * page is initialized.
+        */
+       (volatile u64) rp->vars_pa = __pa(xpc_vars);
+
+       return rp;
+}
+
+
+/*
+ * Change protections to allow IPI operations (and AMO operations on
+ * Shub 1.1 systems).
+ */
+void
+xpc_allow_IPI_ops(void)
+{
+       int node;
+       int nasid;
+
+
+       // >>> Change SH_IPI_ACCESS code to use SAL call once it is available.
+
+       if (is_shub2()) {
+               xpc_sh2_IPI_access0 =
+                       (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH2_IPI_ACCESS0));
+               xpc_sh2_IPI_access1 =
+                       (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH2_IPI_ACCESS1));
+               xpc_sh2_IPI_access2 =
+                       (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH2_IPI_ACCESS2));
+               xpc_sh2_IPI_access3 =
+                       (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH2_IPI_ACCESS3));
+
+               for_each_online_node(node) {
+                       nasid = cnodeid_to_nasid(node);
+                       HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS0),
+                                                               -1UL);
+                       HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS1),
+                                                               -1UL);
+                       HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS2),
+                                                               -1UL);
+                       HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS3),
+                                                               -1UL);
+               }
+
+       } else {
+               xpc_sh1_IPI_access =
+                       (u64) HUB_L((u64 *) LOCAL_MMR_ADDR(SH1_IPI_ACCESS));
+
+               for_each_online_node(node) {
+                       nasid = cnodeid_to_nasid(node);
+                       HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH1_IPI_ACCESS),
+                                                               -1UL);
+
+                       /*
+                        * Since the BIST collides with memory operations on
+                        * SHUB 1.1 sn_change_memprotect() cannot be used.
+                        */
+                       if (enable_shub_wars_1_1()) {
+                               /* open up everything */
+                               xpc_prot_vec[node] = (u64) HUB_L((u64 *)
+                                               GLOBAL_MMR_ADDR(nasid,
+                                               SH1_MD_DQLP_MMR_DIR_PRIVEC0));
+                               HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid,
+                                               SH1_MD_DQLP_MMR_DIR_PRIVEC0),
+                                                               -1UL);
+                               HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid,
+                                               SH1_MD_DQRP_MMR_DIR_PRIVEC0),
+                                                               -1UL);
+                       }
+               }
+       }
+}
+
+
+/*
+ * Restrict protections to disallow IPI operations (and AMO operations on
+ * Shub 1.1 systems).
+ */
+void
+xpc_restrict_IPI_ops(void)
+{
+       int node;
+       int nasid;
+
+
+       // >>> Change SH_IPI_ACCESS code to use SAL call once it is available.
+
+       if (is_shub2()) {
+
+               for_each_online_node(node) {
+                       nasid = cnodeid_to_nasid(node);
+                       HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS0),
+                                                       xpc_sh2_IPI_access0);
+                       HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS1),
+                                                       xpc_sh2_IPI_access1);
+                       HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS2),
+                                                       xpc_sh2_IPI_access2);
+                       HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH2_IPI_ACCESS3),
+                                                       xpc_sh2_IPI_access3);
+               }
+
+       } else {
+
+               for_each_online_node(node) {
+                       nasid = cnodeid_to_nasid(node);
+                       HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid, SH1_IPI_ACCESS),
+                                                       xpc_sh1_IPI_access);
+
+                       if (enable_shub_wars_1_1()) {
+                               HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid,
+                                               SH1_MD_DQLP_MMR_DIR_PRIVEC0),
+                                                       xpc_prot_vec[node]);
+                               HUB_S((u64 *) GLOBAL_MMR_ADDR(nasid,
+                                               SH1_MD_DQRP_MMR_DIR_PRIVEC0),
+                                                       xpc_prot_vec[node]);
+                       }
+               }
+       }
+}
+
+
+/*
+ * At periodic intervals, scan through all active partitions and ensure
+ * their heartbeat is still active.  If not, the partition is deactivated.
+ */
+void
+xpc_check_remote_hb(void)
+{
+       struct xpc_vars *remote_vars;
+       struct xpc_partition *part;
+       partid_t partid;
+       bte_result_t bres;
+
+
+       remote_vars = (struct xpc_vars *) xpc_remote_copy_buffer;
+
+       for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) {
+               if (partid == sn_partition_id) {
+                       continue;
+               }
+
+               part = &xpc_partitions[partid];
+
+               if (part->act_state == XPC_P_INACTIVE ||
+                               part->act_state == XPC_P_DEACTIVATING) {
+                       continue;
+               }
+
+               /* pull the remote_hb cache line */
+               bres = xp_bte_copy(part->remote_vars_pa,
+                                       ia64_tpa((u64) remote_vars),
+                                       XPC_VARS_ALIGNED_SIZE,
+                                       (BTE_NOTIFY | BTE_WACQUIRE), NULL);
+               if (bres != BTE_SUCCESS) {
+                       XPC_DEACTIVATE_PARTITION(part,
+                                               xpc_map_bte_errors(bres));
+                       continue;
+               }
+
+               dev_dbg(xpc_part, "partid = %d, heartbeat = %ld, last_heartbeat"
+                       " = %ld, kdb_status = %ld, HB_mask = 0x%lx\n", partid,
+                       remote_vars->heartbeat, part->last_heartbeat,
+                       remote_vars->kdb_status,
+                       remote_vars->heartbeating_to_mask);
+
+               if (((remote_vars->heartbeat == part->last_heartbeat) &&
+                       (remote_vars->kdb_status == 0)) ||
+                            !XPC_HB_ALLOWED(sn_partition_id, remote_vars)) {
+
+                       XPC_DEACTIVATE_PARTITION(part, xpcNoHeartbeat);
+                       continue;
+               }
+
+               part->last_heartbeat = remote_vars->heartbeat;
+       }
+}
+
+
+/*
+ * Get a copy of the remote partition's rsvd page.
+ *
+ * remote_rp points to a buffer that is cacheline aligned for BTE copies and
+ * assumed to be of size XPC_RSVD_PAGE_ALIGNED_SIZE.
+ */
+static enum xpc_retval
+xpc_get_remote_rp(int nasid, u64 *discovered_nasids,
+               struct xpc_rsvd_page *remote_rp, u64 *remote_rsvd_page_pa)
+{
+       int bres, i;
+
+
+       /* get the reserved page's physical address */
+
+       *remote_rsvd_page_pa = xpc_get_rsvd_page_pa(nasid, (u64) remote_rp,
+                                               XPC_RSVD_PAGE_ALIGNED_SIZE);
+       if (*remote_rsvd_page_pa == 0) {
+               return xpcNoRsvdPageAddr;
+       }
+
+
+       /* pull over the reserved page structure */
+
+       bres = xp_bte_copy(*remote_rsvd_page_pa, ia64_tpa((u64) remote_rp),
+                               XPC_RSVD_PAGE_ALIGNED_SIZE,
+                               (BTE_NOTIFY | BTE_WACQUIRE), NULL);
+       if (bres != BTE_SUCCESS) {
+               return xpc_map_bte_errors(bres);
+       }
+
+
+       if (discovered_nasids != NULL) {
+               for (i = 0; i < XP_NASID_MASK_WORDS; i++) {
+                       discovered_nasids[i] |= remote_rp->part_nasids[i];
+               }
+       }
+
+
+       /* check that the partid is for another partition */
+
+       if (remote_rp->partid < 1 ||
+                               remote_rp->partid > (XP_MAX_PARTITIONS - 1)) {
+               return xpcInvalidPartid;
+       }
+
+       if (remote_rp->partid == sn_partition_id) {
+               return xpcLocalPartid;
+       }
+
+
+       if (XPC_VERSION_MAJOR(remote_rp->version) !=
+                                       XPC_VERSION_MAJOR(XPC_RP_VERSION)) {
+               return xpcBadVersion;
+       }
+
+       return xpcSuccess;
+}
+
+
+/*
+ * Get a copy of the remote partition's XPC variables.
+ *
+ * remote_vars points to a buffer that is cacheline aligned for BTE copies and
+ * assumed to be of size XPC_VARS_ALIGNED_SIZE.
+ */
+static enum xpc_retval
+xpc_get_remote_vars(u64 remote_vars_pa, struct xpc_vars *remote_vars)
+{
+       int bres;
+
+
+       if (remote_vars_pa == 0) {
+               return xpcVarsNotSet;
+       }
+
+
+       /* pull over the cross partition variables */
+
+       bres = xp_bte_copy(remote_vars_pa, ia64_tpa((u64) remote_vars),
+                               XPC_VARS_ALIGNED_SIZE,
+                               (BTE_NOTIFY | BTE_WACQUIRE), NULL);
+       if (bres != BTE_SUCCESS) {
+               return xpc_map_bte_errors(bres);
+       }
+
+       if (XPC_VERSION_MAJOR(remote_vars->version) !=
+                                       XPC_VERSION_MAJOR(XPC_V_VERSION)) {
+               return xpcBadVersion;
+       }
+
+       return xpcSuccess;
+}
+
+
+/*
+ * Prior code has determine the nasid which generated an IPI.  Inspect
+ * that nasid to determine if its partition needs to be activated or
+ * deactivated.
+ *
+ * A partition is consider "awaiting activation" if our partition
+ * flags indicate it is not active and it has a heartbeat.  A
+ * partition is considered "awaiting deactivation" if our partition
+ * flags indicate it is active but it has no heartbeat or it is not
+ * sending its heartbeat to us.
+ *
+ * To determine the heartbeat, the remote nasid must have a properly
+ * initialized reserved page.
+ */
+static void
+xpc_identify_act_IRQ_req(int nasid)
+{
+       struct xpc_rsvd_page *remote_rp;
+       struct xpc_vars *remote_vars;
+       u64 remote_rsvd_page_pa;
+       u64 remote_vars_pa;
+       partid_t partid;
+       struct xpc_partition *part;
+       enum xpc_retval ret;
+
+
+       /* pull over the reserved page structure */
+
+       remote_rp = (struct xpc_rsvd_page *) xpc_remote_copy_buffer;
+
+       ret = xpc_get_remote_rp(nasid, NULL, remote_rp, &remote_rsvd_page_pa);
+       if (ret != xpcSuccess) {
+               dev_warn(xpc_part, "unable to get reserved page from nasid %d, "
+                       "which sent interrupt, reason=%d\n", nasid, ret);
+               return;
+       }
+
+       remote_vars_pa = remote_rp->vars_pa;
+       partid = remote_rp->partid;
+       part = &xpc_partitions[partid];
+
+
+       /* pull over the cross partition variables */
+
+       remote_vars = (struct xpc_vars *) xpc_remote_copy_buffer;
+
+       ret = xpc_get_remote_vars(remote_vars_pa, remote_vars);
+       if (ret != xpcSuccess) {
+
+               dev_warn(xpc_part, "unable to get XPC variables from nasid %d, "
+                       "which sent interrupt, reason=%d\n", nasid, ret);
+
+               XPC_DEACTIVATE_PARTITION(part, ret);
+               return;
+       }
+
+
+       part->act_IRQ_rcvd++;
+
+       dev_dbg(xpc_part, "partid for nasid %d is %d; IRQs = %d; HB = "
+               "%ld:0x%lx\n", (int) nasid, (int) partid, part->act_IRQ_rcvd,
+               remote_vars->heartbeat, remote_vars->heartbeating_to_mask);
+
+
+       if (part->act_state == XPC_P_INACTIVE) {
+
+               part->remote_rp_pa = remote_rsvd_page_pa;
+               dev_dbg(xpc_part, "  remote_rp_pa = 0x%016lx\n",
+                       part->remote_rp_pa);
+
+               part->remote_vars_pa = remote_vars_pa;
+               dev_dbg(xpc_part, "  remote_vars_pa = 0x%016lx\n",
+                       part->remote_vars_pa);
+
+               part->last_heartbeat = remote_vars->heartbeat;
+               dev_dbg(xpc_part, "  last_heartbeat = 0x%016lx\n",
+                       part->last_heartbeat);
+
+               part->remote_vars_part_pa = remote_vars->vars_part_pa;
+               dev_dbg(xpc_part, "  remote_vars_part_pa = 0x%016lx\n",
+                       part->remote_vars_part_pa);
+
+               part->remote_act_nasid = remote_vars->act_nasid;
+               dev_dbg(xpc_part, "  remote_act_nasid = 0x%x\n",
+                       part->remote_act_nasid);
+
+               part->remote_act_phys_cpuid = remote_vars->act_phys_cpuid;
+               dev_dbg(xpc_part, "  remote_act_phys_cpuid = 0x%x\n",
+                       part->remote_act_phys_cpuid);
+
+               part->remote_amos_page_pa = remote_vars->amos_page_pa;
+               dev_dbg(xpc_part, "  remote_amos_page_pa = 0x%lx\n",
+                       part->remote_amos_page_pa);
+
+               xpc_activate_partition(part);
+
+       } else if (part->remote_amos_page_pa != remote_vars->amos_page_pa ||
+                       !XPC_HB_ALLOWED(sn_partition_id, remote_vars)) {
+
+               part->reactivate_nasid = nasid;
+               XPC_DEACTIVATE_PARTITION(part, xpcReactivating);
+       }
+}
+
+
+/*
+ * Loop through the activation AMO variables and process any bits
+ * which are set.  Each bit indicates a nasid sending a partition
+ * activation or deactivation request.
+ *
+ * Return #of IRQs detected.
+ */
+int
+xpc_identify_act_IRQ_sender(void)
+{
+       int word, bit;
+       u64 nasid_mask;
+       u64 nasid;                      /* remote nasid */
+       int n_IRQs_detected = 0;
+       AMO_t *act_amos;
+       struct xpc_rsvd_page *rp = (struct xpc_rsvd_page *) xpc_rsvd_page;
+
+
+       act_amos = xpc_vars->act_amos;
+
+
+       /* scan through act AMO variable looking for non-zero entries */
+       for (word = 0; word < XP_NASID_MASK_WORDS; word++) {
+
+               nasid_mask = xpc_IPI_receive(&act_amos[word]);
+               if (nasid_mask == 0) {
+                       /* no IRQs from nasids in this variable */
+                       continue;
+               }
+
+               dev_dbg(xpc_part, "AMO[%d] gave back 0x%lx\n", word,
+                       nasid_mask);
+
+
+               /*
+                * If this nasid has been added to the machine since
+                * our partition was reset, this will retain the
+                * remote nasid in our reserved pages machine mask.
+                * This is used in the event of module reload.
+                */
+               rp->mach_nasids[word] |= nasid_mask;
+
+
+               /* locate the nasid(s) which sent interrupts */
+
+               for (bit = 0; bit < (8 * sizeof(u64)); bit++) {
+                       if (nasid_mask & (1UL << bit)) {
+                               n_IRQs_detected++;
+                               nasid = XPC_NASID_FROM_W_B(word, bit);
+                               dev_dbg(xpc_part, "interrupt from nasid %ld\n",
+                                       nasid);
+                               xpc_identify_act_IRQ_req(nasid);
+                       }
+               }
+       }
+       return n_IRQs_detected;
+}
+
+
+/*
+ * Mark specified partition as active.
+ */
+enum xpc_retval
+xpc_mark_partition_active(struct xpc_partition *part)
+{
+       unsigned long irq_flags;
+       enum xpc_retval ret;
+
+
+       dev_dbg(xpc_part, "setting partition %d to ACTIVE\n", XPC_PARTID(part));
+
+       spin_lock_irqsave(&part->act_lock, irq_flags);
+       if (part->act_state == XPC_P_ACTIVATING) {
+               part->act_state = XPC_P_ACTIVE;
+               ret = xpcSuccess;
+       } else {
+               DBUG_ON(part->reason == xpcSuccess);
+               ret = part->reason;
+       }
+       spin_unlock_irqrestore(&part->act_lock, irq_flags);
+
+       return ret;
+}
+
+
+/*
+ * Notify XPC that the partition is down.
+ */
+void
+xpc_deactivate_partition(const int line, struct xpc_partition *part,
+                               enum xpc_retval reason)
+{
+       unsigned long irq_flags;
+       partid_t partid = XPC_PARTID(part);
+
+
+       spin_lock_irqsave(&part->act_lock, irq_flags);
+
+       if (part->act_state == XPC_P_INACTIVE) {
+               XPC_SET_REASON(part, reason, line);
+               spin_unlock_irqrestore(&part->act_lock, irq_flags);
+               if (reason == xpcReactivating) {
+                       /* we interrupt ourselves to reactivate partition */
+                       xpc_IPI_send_reactivate(part);
+               }
+               return;
+       }
+       if (part->act_state == XPC_P_DEACTIVATING) {
+               if ((part->reason == xpcUnloading && reason != xpcUnloading) ||
+                                       reason == xpcReactivating) {
+                       XPC_SET_REASON(part, reason, line);
+               }
+               spin_unlock_irqrestore(&part->act_lock, irq_flags);
+               return;
+       }
+
+       part->act_state = XPC_P_DEACTIVATING;
+       XPC_SET_REASON(part, reason, line);
+
+       spin_unlock_irqrestore(&part->act_lock, irq_flags);
+
+       XPC_DISALLOW_HB(partid, xpc_vars);
+
+       dev_dbg(xpc_part, "bringing partition %d down, reason = %d\n", partid,
+               reason);
+
+       xpc_partition_down(part, reason);
+}
+
+
+/*
+ * Mark specified partition as active.
+ */
+void
+xpc_mark_partition_inactive(struct xpc_partition *part)
+{
+       unsigned long irq_flags;
+
+
+       dev_dbg(xpc_part, "setting partition %d to INACTIVE\n",
+               XPC_PARTID(part));
+
+       spin_lock_irqsave(&part->act_lock, irq_flags);
+       part->act_state = XPC_P_INACTIVE;
+       spin_unlock_irqrestore(&part->act_lock, irq_flags);
+       part->remote_rp_pa = 0;
+}
+
+
+/*
+ * SAL has provided a partition and machine mask.  The partition mask
+ * contains a bit for each even nasid in our partition.  The machine
+ * mask contains a bit for each even nasid in the entire machine.
+ *
+ * Using those two bit arrays, we can determine which nasids are
+ * known in the machine.  Each should also have a reserved page
+ * initialized if they are available for partitioning.
+ */
+void
+xpc_discovery(void)
+{
+       void *remote_rp_base;
+       struct xpc_rsvd_page *remote_rp;
+       struct xpc_vars *remote_vars;
+       u64 remote_rsvd_page_pa;
+       u64 remote_vars_pa;
+       int region;
+       int max_regions;
+       int nasid;
+       struct xpc_rsvd_page *rp;
+       partid_t partid;
+       struct xpc_partition *part;
+       u64 *discovered_nasids;
+       enum xpc_retval ret;
+
+
+       remote_rp = xpc_kmalloc_cacheline_aligned(XPC_RSVD_PAGE_ALIGNED_SIZE,
+                                               GFP_KERNEL, &remote_rp_base);
+       if (remote_rp == NULL) {
+               return;
+       }
+       remote_vars = (struct xpc_vars *) remote_rp;
+
+
+       discovered_nasids = kmalloc(sizeof(u64) * XP_NASID_MASK_WORDS,
+                                                       GFP_KERNEL);
+       if (discovered_nasids == NULL) {
+               kfree(remote_rp_base);
+               return;
+       }
+       memset(discovered_nasids, 0, sizeof(u64) * XP_NASID_MASK_WORDS);
+
+       rp = (struct xpc_rsvd_page *) xpc_rsvd_page;
+
+       /*
+        * The term 'region' in this context refers to the minimum number of
+        * nodes that can comprise an access protection grouping. The access
+        * protection is in regards to memory, IOI and IPI.
+        */
+//>>> move the next two #defines into either include/asm-ia64/sn/arch.h or
+//>>> include/asm-ia64/sn/addrs.h
+#define SH1_MAX_REGIONS                64
+#define SH2_MAX_REGIONS                256
+       max_regions = is_shub2() ? SH2_MAX_REGIONS : SH1_MAX_REGIONS;
+
+       for (region = 0; region < max_regions; region++) {
+
+               if ((volatile int) xpc_exiting) {
+                       break;
+               }
+
+               dev_dbg(xpc_part, "searching region %d\n", region);
+
+               for (nasid = (region * sn_region_size * 2);
+                    nasid < ((region + 1) * sn_region_size * 2);
+                    nasid += 2) {
+
+                       if ((volatile int) xpc_exiting) {
+                               break;
+                       }
+
+                       dev_dbg(xpc_part, "checking nasid %d\n", nasid);
+
+
+                       if (XPC_NASID_IN_ARRAY(nasid, rp->part_nasids)) {
+                               dev_dbg(xpc_part, "PROM indicates Nasid %d is "
+                                       "part of the local partition; skipping "
+                                       "region\n", nasid);
+                               break;
+                       }
+
+                       if (!(XPC_NASID_IN_ARRAY(nasid, rp->mach_nasids))) {
+                               dev_dbg(xpc_part, "PROM indicates Nasid %d was "
+                                       "not on Numa-Link network at reset\n",
+                                       nasid);
+                               continue;
+                       }
+
+                       if (XPC_NASID_IN_ARRAY(nasid, discovered_nasids)) {
+                               dev_dbg(xpc_part, "Nasid %d is part of a "
+                                       "partition which was previously "
+                                       "discovered\n", nasid);
+                               continue;
+                       }
+
+
+                       /* pull over the reserved page structure */
+
+                       ret = xpc_get_remote_rp(nasid, discovered_nasids,
+                                             remote_rp, &remote_rsvd_page_pa);
+                       if (ret != xpcSuccess) {
+                               dev_dbg(xpc_part, "unable to get reserved page "
+                                       "from nasid %d, reason=%d\n", nasid,
+                                       ret);
+
+                               if (ret == xpcLocalPartid) {
+                                       break;
+                               }
+                               continue;
+                       }
+
+                       remote_vars_pa = remote_rp->vars_pa;
+
+                       partid = remote_rp->partid;
+                       part = &xpc_partitions[partid];
+
+
+                       /* pull over the cross partition variables */
+
+                       ret = xpc_get_remote_vars(remote_vars_pa, remote_vars);
+                       if (ret != xpcSuccess) {
+                               dev_dbg(xpc_part, "unable to get XPC variables "
+                                       "from nasid %d, reason=%d\n", nasid,
+                                       ret);
+
+                               XPC_DEACTIVATE_PARTITION(part, ret);
+                               continue;
+                       }
+
+                       if (part->act_state != XPC_P_INACTIVE) {
+                               dev_dbg(xpc_part, "partition %d on nasid %d is "
+                                       "already activating\n", partid, nasid);
+                               break;
+                       }
+
+                       /*
+                        * Register the remote partition's AMOs with SAL so it
+                        * can handle and cleanup errors within that address
+                        * range should the remote partition go down. We don't
+                        * unregister this range because it is difficult to
+                        * tell when outstanding writes to the remote partition
+                        * are finished and thus when it is thus safe to
+                        * unregister. This should not result in wasted space
+                        * in the SAL xp_addr_region table because we should
+                        * get the same page for remote_act_amos_pa after
+                        * module reloads and system reboots.
+                        */
+                       if (sn_register_xp_addr_region(
+                                           remote_vars->amos_page_pa,
+                                                       PAGE_SIZE, 1) < 0) {
+                               dev_dbg(xpc_part, "partition %d failed to "
+                                       "register xp_addr region 0x%016lx\n",
+                                       partid, remote_vars->amos_page_pa);
+
+                               XPC_SET_REASON(part, xpcPhysAddrRegFailed,
+                                               __LINE__);
+                               break;
+                       }
+
+                       /*
+                        * The remote nasid is valid and available.
+                        * Send an interrupt to that nasid to notify
+                        * it that we are ready to begin activation.
+                        */
+                       dev_dbg(xpc_part, "sending an interrupt to AMO 0x%lx, "
+                               "nasid %d, phys_cpuid 0x%x\n",
+                               remote_vars->amos_page_pa,
+                               remote_vars->act_nasid,
+                               remote_vars->act_phys_cpuid);
+
+                       xpc_IPI_send_activate(remote_vars);
+               }
+       }
+
+       kfree(discovered_nasids);
+       kfree(remote_rp_base);
+}
+
+
+/*
+ * Given a partid, get the nasids owned by that partition from the
+ * remote partition's reserved page.
+ */
+enum xpc_retval
+xpc_initiate_partid_to_nasids(partid_t partid, void *nasid_mask)
+{
+       struct xpc_partition *part;
+       u64 part_nasid_pa;
+       int bte_res;
+
+
+       part = &xpc_partitions[partid];
+       if (part->remote_rp_pa == 0) {
+               return xpcPartitionDown;
+       }
+
+       part_nasid_pa = part->remote_rp_pa +
+               (u64) &((struct xpc_rsvd_page *) 0)->part_nasids;
+
+       bte_res = xp_bte_copy(part_nasid_pa, ia64_tpa((u64) nasid_mask),
+                               L1_CACHE_ALIGN(XP_NASID_MASK_BYTES),
+                               (BTE_NOTIFY | BTE_WACQUIRE), NULL);
+
+       return xpc_map_bte_errors(bte_res);
+}
+
diff --git a/arch/ia64/sn/kernel/xpnet.c b/arch/ia64/sn/kernel/xpnet.c
new file mode 100644 (file)
index 0000000..78c13d6
--- /dev/null
@@ -0,0 +1,715 @@
+/*
+ * 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,2001-2005 Silicon Graphics, Inc. All rights reserved.
+ */
+
+
+/*
+ * Cross Partition Network Interface (XPNET) support
+ *
+ *     XPNET provides a virtual network layered on top of the Cross
+ *     Partition communication layer.
+ *
+ *     XPNET provides direct point-to-point and broadcast-like support
+ *     for an ethernet-like device.  The ethernet broadcast medium is
+ *     replaced with a point-to-point message structure which passes
+ *     pointers to a DMA-capable block that a remote partition should
+ *     retrieve and pass to the upper level networking layer.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/smp.h>
+#include <linux/string.h>
+#include <asm/sn/bte.h>
+#include <asm/sn/io.h>
+#include <asm/sn/sn_sal.h>
+#include <asm/types.h>
+#include <asm/atomic.h>
+#include <asm/sn/xp.h>
+
+
+/*
+ * The message payload transferred by XPC.
+ *
+ * buf_pa is the physical address where the DMA should pull from.
+ *
+ * NOTE: for performance reasons, buf_pa should _ALWAYS_ begin on a
+ * cacheline boundary.  To accomplish this, we record the number of
+ * bytes from the beginning of the first cacheline to the first useful
+ * byte of the skb (leadin_ignore) and the number of bytes from the
+ * last useful byte of the skb to the end of the last cacheline
+ * (tailout_ignore).
+ *
+ * size is the number of bytes to transfer which includes the skb->len
+ * (useful bytes of the senders skb) plus the leadin and tailout
+ */
+struct xpnet_message {
+       u16 version;            /* Version for this message */
+       u16 embedded_bytes;     /* #of bytes embedded in XPC message */
+       u32 magic;              /* Special number indicating this is xpnet */
+       u64 buf_pa;             /* phys address of buffer to retrieve */
+       u32 size;               /* #of bytes in buffer */
+       u8 leadin_ignore;       /* #of bytes to ignore at the beginning */
+       u8 tailout_ignore;      /* #of bytes to ignore at the end */
+       unsigned char data;     /* body of small packets */
+};
+
+/*
+ * Determine the size of our message, the cacheline aligned size,
+ * and then the number of message will request from XPC.
+ *
+ * XPC expects each message to exist in an individual cacheline.
+ */
+#define XPNET_MSG_SIZE         (L1_CACHE_BYTES - XPC_MSG_PAYLOAD_OFFSET)
+#define XPNET_MSG_DATA_MAX     \
+               (XPNET_MSG_SIZE - (u64)(&((struct xpnet_message *)0)->data))
+#define XPNET_MSG_ALIGNED_SIZE (L1_CACHE_ALIGN(XPNET_MSG_SIZE))
+#define XPNET_MSG_NENTRIES     (PAGE_SIZE / XPNET_MSG_ALIGNED_SIZE)
+
+
+#define XPNET_MAX_KTHREADS     (XPNET_MSG_NENTRIES + 1)
+#define XPNET_MAX_IDLE_KTHREADS        (XPNET_MSG_NENTRIES + 1)
+
+/*
+ * Version number of XPNET implementation. XPNET can always talk to versions
+ * with same major #, and never talk to versions with a different version.
+ */
+#define _XPNET_VERSION(_major, _minor) (((_major) << 4) | (_minor))
+#define XPNET_VERSION_MAJOR(_v)                ((_v) >> 4)
+#define XPNET_VERSION_MINOR(_v)                ((_v) & 0xf)
+
+#define        XPNET_VERSION _XPNET_VERSION(1,0)               /* version 1.0 */
+#define        XPNET_VERSION_EMBED _XPNET_VERSION(1,1)         /* version 1.1 */
+#define XPNET_MAGIC    0x88786984 /* "XNET" */
+
+#define XPNET_VALID_MSG(_m)                                                 \
+   ((XPNET_VERSION_MAJOR(_m->version) == XPNET_VERSION_MAJOR(XPNET_VERSION)) \
+    && (msg->magic == XPNET_MAGIC))
+
+#define XPNET_DEVICE_NAME              "xp0"
+
+
+/*
+ * When messages are queued with xpc_send_notify, a kmalloc'd buffer
+ * of the following type is passed as a notification cookie.  When the
+ * notification function is called, we use the cookie to decide
+ * whether all outstanding message sends have completed.  The skb can
+ * then be released.
+ */
+struct xpnet_pending_msg {
+       struct list_head free_list;
+       struct sk_buff *skb;
+       atomic_t use_count;
+};
+
+/* driver specific structure pointed to by the device structure */
+struct xpnet_dev_private {
+       struct net_device_stats stats;
+};
+
+struct net_device *xpnet_device;
+
+/*
+ * When we are notified of other partitions activating, we add them to
+ * our bitmask of partitions to which we broadcast.
+ */
+static u64 xpnet_broadcast_partitions;
+/* protect above */
+static spinlock_t xpnet_broadcast_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Since the Block Transfer Engine (BTE) is being used for the transfer
+ * and it relies upon cache-line size transfers, we need to reserve at
+ * least one cache-line for head and tail alignment.  The BTE is
+ * limited to 8MB transfers.
+ *
+ * Testing has shown that changing MTU to greater than 64KB has no effect
+ * on TCP as the two sides negotiate a Max Segment Size that is limited
+ * to 64K.  Other protocols May use packets greater than this, but for
+ * now, the default is 64KB.
+ */
+#define XPNET_MAX_MTU (0x800000UL - L1_CACHE_BYTES)
+/* 32KB has been determined to be the ideal */
+#define XPNET_DEF_MTU (0x8000UL)
+
+
+/*
+ * The partition id is encapsulated in the MAC address.  The following
+ * define locates the octet the partid is in.
+ */
+#define XPNET_PARTID_OCTET     1
+#define XPNET_LICENSE_OCTET    2
+
+
+/*
+ * Define the XPNET debug device structure that is to be used with dev_dbg(),
+ * dev_err(), dev_warn(), and dev_info().
+ */
+struct device_driver xpnet_dbg_name = {
+       .name = "xpnet"
+};
+
+struct device xpnet_dbg_subname = {
+       .bus_id = {0},                  /* set to "" */
+       .driver = &xpnet_dbg_name
+};
+
+struct device *xpnet = &xpnet_dbg_subname;
+
+/*
+ * Packet was recevied by XPC and forwarded to us.
+ */
+static void
+xpnet_receive(partid_t partid, int channel, struct xpnet_message *msg)
+{
+       struct sk_buff *skb;
+       bte_result_t bret;
+       struct xpnet_dev_private *priv =
+               (struct xpnet_dev_private *) xpnet_device->priv;
+
+
+       if (!XPNET_VALID_MSG(msg)) {
+               /*
+                * Packet with a different XPC version.  Ignore.
+                */
+               xpc_received(partid, channel, (void *) msg);
+
+               priv->stats.rx_errors++;
+
+               return;
+       }
+       dev_dbg(xpnet, "received 0x%lx, %d, %d, %d\n", msg->buf_pa, msg->size,
+               msg->leadin_ignore, msg->tailout_ignore);
+
+
+       /* reserve an extra cache line */
+       skb = dev_alloc_skb(msg->size + L1_CACHE_BYTES);
+       if (!skb) {
+               dev_err(xpnet, "failed on dev_alloc_skb(%d)\n",
+                       msg->size + L1_CACHE_BYTES);
+
+               xpc_received(partid, channel, (void *) msg);
+
+               priv->stats.rx_errors++;
+
+               return;
+       }
+
+       /*
+        * The allocated skb has some reserved space.
+        * In order to use bte_copy, we need to get the
+        * skb->data pointer moved forward.
+        */
+       skb_reserve(skb, (L1_CACHE_BYTES - ((u64)skb->data &
+                                           (L1_CACHE_BYTES - 1)) +
+                         msg->leadin_ignore));
+
+       /*
+        * Update the tail pointer to indicate data actually
+        * transferred.
+        */
+       skb_put(skb, (msg->size - msg->leadin_ignore - msg->tailout_ignore));
+
+       /*
+        * Move the data over from the the other side.
+        */
+       if ((XPNET_VERSION_MINOR(msg->version) == 1) &&
+                                               (msg->embedded_bytes != 0)) {
+               dev_dbg(xpnet, "copying embedded message. memcpy(0x%p, 0x%p, "
+                       "%lu)\n", skb->data, &msg->data,
+                       (size_t) msg->embedded_bytes);
+
+               memcpy(skb->data, &msg->data, (size_t) msg->embedded_bytes);
+       } else {
+               dev_dbg(xpnet, "transferring buffer to the skb->data area;\n\t"
+                       "bte_copy(0x%p, 0x%p, %hu)\n", (void *)msg->buf_pa,
+                       (void *)__pa((u64)skb->data & ~(L1_CACHE_BYTES - 1)),
+                       msg->size);
+
+               bret = bte_copy(msg->buf_pa,
+                               __pa((u64)skb->data & ~(L1_CACHE_BYTES - 1)),
+                               msg->size, (BTE_NOTIFY | BTE_WACQUIRE), NULL);
+
+               if (bret != BTE_SUCCESS) {
+                       // >>> Need better way of cleaning skb.  Currently skb
+                       // >>> appears in_use and we can't just call
+                       // >>> dev_kfree_skb.
+                       dev_err(xpnet, "bte_copy(0x%p, 0x%p, 0x%hx) returned "
+                               "error=0x%x\n", (void *)msg->buf_pa,
+                               (void *)__pa((u64)skb->data &
+                                                       ~(L1_CACHE_BYTES - 1)),
+                               msg->size, bret);
+
+                       xpc_received(partid, channel, (void *) msg);
+
+                       priv->stats.rx_errors++;
+
+                       return;
+               }
+       }
+
+       dev_dbg(xpnet, "<skb->head=0x%p skb->data=0x%p skb->tail=0x%p "
+               "skb->end=0x%p skb->len=%d\n", (void *) skb->head,
+               (void *) skb->data, (void *) skb->tail, (void *) skb->end,
+               skb->len);
+
+       skb->dev = xpnet_device;
+       skb->protocol = eth_type_trans(skb, xpnet_device);
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       dev_dbg(xpnet, "passing skb to network layer; \n\tskb->head=0x%p "
+               "skb->data=0x%p skb->tail=0x%p skb->end=0x%p skb->len=%d\n",
+               (void *) skb->head, (void *) skb->data, (void *) skb->tail,
+               (void *) skb->end, skb->len);
+
+
+       xpnet_device->last_rx = jiffies;
+       priv->stats.rx_packets++;
+       priv->stats.rx_bytes += skb->len + ETH_HLEN;
+
+       netif_rx_ni(skb);
+       xpc_received(partid, channel, (void *) msg);
+}
+
+
+/*
+ * This is the handler which XPC calls during any sort of change in
+ * state or message reception on a connection.
+ */
+static void
+xpnet_connection_activity(enum xpc_retval reason, partid_t partid, int channel,
+                         void *data, void *key)
+{
+       long bp;
+
+
+       DBUG_ON(partid <= 0 || partid >= XP_MAX_PARTITIONS);
+       DBUG_ON(channel != XPC_NET_CHANNEL);
+
+       switch(reason) {
+       case xpcMsgReceived:    /* message received */
+               DBUG_ON(data == NULL);
+
+               xpnet_receive(partid, channel, (struct xpnet_message *) data);
+               break;
+
+       case xpcConnected:      /* connection completed to a partition */
+               spin_lock_bh(&xpnet_broadcast_lock);
+               xpnet_broadcast_partitions |= 1UL << (partid -1 );
+               bp = xpnet_broadcast_partitions;
+               spin_unlock_bh(&xpnet_broadcast_lock);
+
+               netif_carrier_on(xpnet_device);
+
+               dev_dbg(xpnet, "%s connection created to partition %d; "
+                       "xpnet_broadcast_partitions=0x%lx\n",
+                       xpnet_device->name, partid, bp);
+               break;
+
+       default:
+               spin_lock_bh(&xpnet_broadcast_lock);
+               xpnet_broadcast_partitions &= ~(1UL << (partid -1 ));
+               bp = xpnet_broadcast_partitions;
+               spin_unlock_bh(&xpnet_broadcast_lock);
+
+               if (bp == 0) {
+                       netif_carrier_off(xpnet_device);
+               }
+
+               dev_dbg(xpnet, "%s disconnected from partition %d; "
+                       "xpnet_broadcast_partitions=0x%lx\n",
+                       xpnet_device->name, partid, bp);
+               break;
+
+       }
+}
+
+
+static int
+xpnet_dev_open(struct net_device *dev)
+{
+       enum xpc_retval ret;
+
+
+       dev_dbg(xpnet, "calling xpc_connect(%d, 0x%p, NULL, %ld, %ld, %d, "
+               "%d)\n", XPC_NET_CHANNEL, xpnet_connection_activity,
+               XPNET_MSG_SIZE, XPNET_MSG_NENTRIES, XPNET_MAX_KTHREADS,
+               XPNET_MAX_IDLE_KTHREADS);
+
+       ret = xpc_connect(XPC_NET_CHANNEL, xpnet_connection_activity, NULL,
+                         XPNET_MSG_SIZE, XPNET_MSG_NENTRIES,
+                         XPNET_MAX_KTHREADS, XPNET_MAX_IDLE_KTHREADS);
+       if (ret != xpcSuccess) {
+               dev_err(xpnet, "ifconfig up of %s failed on XPC connect, "
+                       "ret=%d\n", dev->name, ret);
+
+               return -ENOMEM;
+       }
+
+       dev_dbg(xpnet, "ifconfig up of %s; XPC connected\n", dev->name);
+
+       return 0;
+}
+
+
+static int
+xpnet_dev_stop(struct net_device *dev)
+{
+       xpc_disconnect(XPC_NET_CHANNEL);
+
+       dev_dbg(xpnet, "ifconfig down of %s; XPC disconnected\n", dev->name);
+
+       return 0;
+}
+
+
+static int
+xpnet_dev_change_mtu(struct net_device *dev, int new_mtu)
+{
+       /* 68 comes from min TCP+IP+MAC header */
+       if ((new_mtu < 68) || (new_mtu > XPNET_MAX_MTU)) {
+               dev_err(xpnet, "ifconfig %s mtu %d failed; value must be "
+                       "between 68 and %ld\n", dev->name, new_mtu,
+                       XPNET_MAX_MTU);
+               return -EINVAL;
+       }
+
+       dev->mtu = new_mtu;
+       dev_dbg(xpnet, "ifconfig %s mtu set to %d\n", dev->name, new_mtu);
+       return 0;
+}
+
+
+/*
+ * Required for the net_device structure.
+ */
+static int
+xpnet_dev_set_config(struct net_device *dev, struct ifmap *new_map)
+{
+       return 0;
+}
+
+
+/*
+ * Return statistics to the caller.
+ */
+static struct net_device_stats *
+xpnet_dev_get_stats(struct net_device *dev)
+{
+       struct xpnet_dev_private *priv;
+
+
+       priv = (struct xpnet_dev_private *) dev->priv;
+
+       return &priv->stats;
+}
+
+
+/*
+ * Notification that the other end has received the message and
+ * DMA'd the skb information.  At this point, they are done with
+ * our side.  When all recipients are done processing, we
+ * release the skb and then release our pending message structure.
+ */
+static void
+xpnet_send_completed(enum xpc_retval reason, partid_t partid, int channel,
+                       void *__qm)
+{
+       struct xpnet_pending_msg *queued_msg =
+               (struct xpnet_pending_msg *) __qm;
+
+
+       DBUG_ON(queued_msg == NULL);
+
+       dev_dbg(xpnet, "message to %d notified with reason %d\n",
+               partid, reason);
+
+       if (atomic_dec_return(&queued_msg->use_count) == 0) {
+               dev_dbg(xpnet, "all acks for skb->head=-x%p\n",
+                       (void *) queued_msg->skb->head);
+
+               dev_kfree_skb_any(queued_msg->skb);
+               kfree(queued_msg);
+       }
+}
+
+
+/*
+ * Network layer has formatted a packet (skb) and is ready to place it
+ * "on the wire".  Prepare and send an xpnet_message to all partitions
+ * which have connected with us and are targets of this packet.
+ *
+ * MAC-NOTE:  For the XPNET driver, the MAC address contains the
+ * destination partition_id.  If the destination partition id word
+ * is 0xff, this packet is to broadcast to all partitions.
+ */
+static int
+xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct xpnet_pending_msg *queued_msg;
+       enum xpc_retval ret;
+       struct xpnet_message *msg;
+       u64 start_addr, end_addr;
+       long dp;
+       u8 second_mac_octet;
+       partid_t dest_partid;
+       struct xpnet_dev_private *priv;
+       u16 embedded_bytes;
+
+
+       priv = (struct xpnet_dev_private *) dev->priv;
+
+
+       dev_dbg(xpnet, ">skb->head=0x%p skb->data=0x%p skb->tail=0x%p "
+               "skb->end=0x%p skb->len=%d\n", (void *) skb->head,
+               (void *) skb->data, (void *) skb->tail, (void *) skb->end,
+               skb->len);
+
+
+       /*
+        * The xpnet_pending_msg tracks how many outstanding
+        * xpc_send_notifies are relying on this skb.  When none
+        * remain, release the skb.
+        */
+       queued_msg = kmalloc(sizeof(struct xpnet_pending_msg), GFP_ATOMIC);
+       if (queued_msg == NULL) {
+               dev_warn(xpnet, "failed to kmalloc %ld bytes; dropping "
+                       "packet\n", sizeof(struct xpnet_pending_msg));
+
+               priv->stats.tx_errors++;
+
+               return -ENOMEM;
+       }
+
+
+       /* get the beginning of the first cacheline and end of last */
+       start_addr = ((u64) skb->data & ~(L1_CACHE_BYTES - 1));
+       end_addr = L1_CACHE_ALIGN((u64) skb->tail);
+
+       /* calculate how many bytes to embed in the XPC message */
+       embedded_bytes = 0;
+       if (unlikely(skb->len <= XPNET_MSG_DATA_MAX)) {
+               /* skb->data does fit so embed */
+               embedded_bytes = skb->len;
+       }
+
+
+       /*
+        * Since the send occurs asynchronously, we set the count to one
+        * and begin sending.  Any sends that happen to complete before
+        * we are done sending will not free the skb.  We will be left
+        * with that task during exit.  This also handles the case of
+        * a packet destined for a partition which is no longer up.
+        */
+       atomic_set(&queued_msg->use_count, 1);
+       queued_msg->skb = skb;
+
+
+       second_mac_octet = skb->data[XPNET_PARTID_OCTET];
+       if (second_mac_octet == 0xff) {
+               /* we are being asked to broadcast to all partitions */
+               dp = xpnet_broadcast_partitions;
+       } else if (second_mac_octet != 0) {
+               dp = xpnet_broadcast_partitions &
+                                       (1UL << (second_mac_octet - 1));
+       } else {
+               /* 0 is an invalid partid.  Ignore */
+               dp = 0;
+       }
+       dev_dbg(xpnet, "destination Partitions mask (dp) = 0x%lx\n", dp);
+
+       /*
+        * If we wanted to allow promiscous mode to work like an
+        * unswitched network, this would be a good point to OR in a
+        * mask of partitions which should be receiving all packets.
+        */
+
+       /*
+        * Main send loop.
+        */
+       for (dest_partid = 1; dp && dest_partid < XP_MAX_PARTITIONS;
+            dest_partid++) {
+
+
+               if (!(dp & (1UL << (dest_partid - 1)))) {
+                       /* not destined for this partition */
+                       continue;
+               }
+
+               /* remove this partition from the destinations mask */
+               dp &= ~(1UL << (dest_partid - 1));
+
+
+               /* found a partition to send to */
+
+               ret = xpc_allocate(dest_partid, XPC_NET_CHANNEL,
+                                  XPC_NOWAIT, (void **)&msg);
+               if (unlikely(ret != xpcSuccess)) {
+                       continue;
+               }
+
+               msg->embedded_bytes = embedded_bytes;
+               if (unlikely(embedded_bytes != 0)) {
+                       msg->version = XPNET_VERSION_EMBED;
+                       dev_dbg(xpnet, "calling memcpy(0x%p, 0x%p, 0x%lx)\n",
+                               &msg->data, skb->data, (size_t) embedded_bytes);
+                       memcpy(&msg->data, skb->data, (size_t) embedded_bytes);
+               } else {
+                       msg->version = XPNET_VERSION;
+               }
+               msg->magic = XPNET_MAGIC;
+               msg->size = end_addr - start_addr;
+               msg->leadin_ignore = (u64) skb->data - start_addr;
+               msg->tailout_ignore = end_addr - (u64) skb->tail;
+               msg->buf_pa = __pa(start_addr);
+
+               dev_dbg(xpnet, "sending XPC message to %d:%d\nmsg->buf_pa="
+                       "0x%lx, msg->size=%u, msg->leadin_ignore=%u, "
+                       "msg->tailout_ignore=%u\n", dest_partid,
+                       XPC_NET_CHANNEL, msg->buf_pa, msg->size,
+                       msg->leadin_ignore, msg->tailout_ignore);
+
+
+               atomic_inc(&queued_msg->use_count);
+
+               ret = xpc_send_notify(dest_partid, XPC_NET_CHANNEL, msg,
+                                     xpnet_send_completed, queued_msg);
+               if (unlikely(ret != xpcSuccess)) {
+                       atomic_dec(&queued_msg->use_count);
+                       continue;
+               }
+
+       }
+
+       if (atomic_dec_return(&queued_msg->use_count) == 0) {
+               dev_dbg(xpnet, "no partitions to receive packet destined for "
+                       "%d\n", dest_partid);
+
+
+               dev_kfree_skb(skb);
+               kfree(queued_msg);
+       }
+
+       priv->stats.tx_packets++;
+       priv->stats.tx_bytes += skb->len;
+
+       return 0;
+}
+
+
+/*
+ * Deal with transmit timeouts coming from the network layer.
+ */
+static void
+xpnet_dev_tx_timeout (struct net_device *dev)
+{
+       struct xpnet_dev_private *priv;
+
+
+       priv = (struct xpnet_dev_private *) dev->priv;
+
+       priv->stats.tx_errors++;
+       return;
+}
+
+
+static int __init
+xpnet_init(void)
+{
+       int i;
+       u32 license_num;
+       int result = -ENOMEM;
+
+
+       dev_info(xpnet, "registering network device %s\n", XPNET_DEVICE_NAME);
+
+       /*
+        * use ether_setup() to init the majority of our device
+        * structure and then override the necessary pieces.
+        */
+       xpnet_device = alloc_netdev(sizeof(struct xpnet_dev_private),
+                                   XPNET_DEVICE_NAME, ether_setup);
+       if (xpnet_device == NULL) {
+               return -ENOMEM;
+       }
+
+       netif_carrier_off(xpnet_device);
+
+       xpnet_device->mtu = XPNET_DEF_MTU;
+       xpnet_device->change_mtu = xpnet_dev_change_mtu;
+       xpnet_device->open = xpnet_dev_open;
+       xpnet_device->get_stats = xpnet_dev_get_stats;
+       xpnet_device->stop = xpnet_dev_stop;
+       xpnet_device->hard_start_xmit = xpnet_dev_hard_start_xmit;
+       xpnet_device->tx_timeout = xpnet_dev_tx_timeout;
+       xpnet_device->set_config = xpnet_dev_set_config;
+
+       /*
+        * Multicast assumes the LSB of the first octet is set for multicast
+        * MAC addresses.  We chose the first octet of the MAC to be unlikely
+        * to collide with any vendor's officially issued MAC.
+        */
+       xpnet_device->dev_addr[0] = 0xfe;
+       xpnet_device->dev_addr[XPNET_PARTID_OCTET] = sn_partition_id;
+       license_num = sn_partition_serial_number_val();
+       for (i = 3; i >= 0; i--) {
+               xpnet_device->dev_addr[XPNET_LICENSE_OCTET + i] =
+                                                       license_num & 0xff;
+               license_num = license_num >> 8;
+       }
+
+       /*
+        * ether_setup() sets this to a multicast device.  We are
+        * really not supporting multicast at this time.
+        */
+       xpnet_device->flags &= ~IFF_MULTICAST;
+
+       /*
+        * No need to checksum as it is a DMA transfer.  The BTE will
+        * report an error if the data is not retrievable and the
+        * packet will be dropped.
+        */
+       xpnet_device->features = NETIF_F_NO_CSUM;
+
+       result = register_netdev(xpnet_device);
+       if (result != 0) {
+               free_netdev(xpnet_device);
+       }
+
+       return result;
+}
+module_init(xpnet_init);
+
+
+static void __exit
+xpnet_exit(void)
+{
+       dev_info(xpnet, "unregistering network device %s\n",
+               xpnet_device[0].name);
+
+       unregister_netdev(xpnet_device);
+
+       free_netdev(xpnet_device);
+}
+module_exit(xpnet_exit);
+
+
+MODULE_AUTHOR("Silicon Graphics, Inc.");
+MODULE_DESCRIPTION("Cross Partition Network adapter (XPNET)");
+MODULE_LICENSE("GPL");
+
diff --git a/arch/ia64/sn/pci/tioca_provider.c b/arch/ia64/sn/pci/tioca_provider.c
new file mode 100644 (file)
index 0000000..8dae9eb
--- /dev/null
@@ -0,0 +1,668 @@
+/*
+ * 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-2005 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 <asm/sn/addrs.h>
+#include <asm/sn/pcidev.h>
+#include <asm/sn/pcibus_provider_defs.h>
+#include <asm/sn/tioca_provider.h>
+
+uint32_t tioca_gart_found;
+EXPORT_SYMBOL(tioca_gart_found);       /* used by agp-sgi */
+
+LIST_HEAD(tioca_list);
+EXPORT_SYMBOL(tioca_list);     /* used by agp-sgi */
+
+static int tioca_gart_init(struct tioca_kernel *);
+
+/**
+ * tioca_gart_init - Initialize SGI TIOCA GART
+ * @tioca_common: ptr to common prom/kernel struct identifying the 
+ *
+ * If the indicated tioca has devices present, initialize its associated
+ * GART MMR's and kernel memory.
+ */
+static int
+tioca_gart_init(struct tioca_kernel *tioca_kern)
+{
+       uint64_t ap_reg;
+       uint64_t offset;
+       struct page *tmp;
+       struct tioca_common *tioca_common;
+       volatile struct tioca *ca_base;
+
+       tioca_common = tioca_kern->ca_common;
+       ca_base = (struct tioca *)tioca_common->ca_common.bs_base;
+
+       if (list_empty(tioca_kern->ca_devices))
+               return 0;
+
+       ap_reg = 0;
+
+       /*
+        * Validate aperature size
+        */
+
+       switch (CA_APERATURE_SIZE >> 20) {
+       case 4:
+               ap_reg |= (0x3ff << CA_GART_AP_SIZE_SHFT);      /* 4MB */
+               break;
+       case 8:
+               ap_reg |= (0x3fe << CA_GART_AP_SIZE_SHFT);      /* 8MB */
+               break;
+       case 16:
+               ap_reg |= (0x3fc << CA_GART_AP_SIZE_SHFT);      /* 16MB */
+               break;
+       case 32:
+               ap_reg |= (0x3f8 << CA_GART_AP_SIZE_SHFT);      /* 32 MB */
+               break;
+       case 64:
+               ap_reg |= (0x3f0 << CA_GART_AP_SIZE_SHFT);      /* 64 MB */
+               break;
+       case 128:
+               ap_reg |= (0x3e0 << CA_GART_AP_SIZE_SHFT);      /* 128 MB */
+               break;
+       case 256:
+               ap_reg |= (0x3c0 << CA_GART_AP_SIZE_SHFT);      /* 256 MB */
+               break;
+       case 512:
+               ap_reg |= (0x380 << CA_GART_AP_SIZE_SHFT);      /* 512 MB */
+               break;
+       case 1024:
+               ap_reg |= (0x300 << CA_GART_AP_SIZE_SHFT);      /* 1GB */
+               break;
+       case 2048:
+               ap_reg |= (0x200 << CA_GART_AP_SIZE_SHFT);      /* 2GB */
+               break;
+       case 4096:
+               ap_reg |= (0x000 << CA_GART_AP_SIZE_SHFT);      /* 4 GB */
+               break;
+       default:
+               printk(KERN_ERR "%s:  Invalid CA_APERATURE_SIZE "
+                      "0x%lx\n", __FUNCTION__, (ulong) CA_APERATURE_SIZE);
+               return -1;
+       }
+
+       /*
+        * Set up other aperature parameters
+        */
+
+       if (PAGE_SIZE >= 16384) {
+               tioca_kern->ca_ap_pagesize = 16384;
+               ap_reg |= CA_GART_PAGE_SIZE;
+       } else {
+               tioca_kern->ca_ap_pagesize = 4096;
+       }
+
+       tioca_kern->ca_ap_size = CA_APERATURE_SIZE;
+       tioca_kern->ca_ap_bus_base = CA_APERATURE_BASE;
+       tioca_kern->ca_gart_entries =
+           tioca_kern->ca_ap_size / tioca_kern->ca_ap_pagesize;
+
+       ap_reg |= (CA_GART_AP_ENB_AGP | CA_GART_AP_ENB_PCI);
+       ap_reg |= tioca_kern->ca_ap_bus_base;
+
+       /*
+        * Allocate and set up the GART
+        */
+
+       tioca_kern->ca_gart_size = tioca_kern->ca_gart_entries * sizeof(u64);
+       tmp =
+           alloc_pages_node(tioca_kern->ca_closest_node,
+                            GFP_KERNEL | __GFP_ZERO,
+                            get_order(tioca_kern->ca_gart_size));
+
+       if (!tmp) {
+               printk(KERN_ERR "%s:  Could not allocate "
+                      "%lu bytes (order %d) for GART\n",
+                      __FUNCTION__,
+                      tioca_kern->ca_gart_size,
+                      get_order(tioca_kern->ca_gart_size));
+               return -ENOMEM;
+       }
+
+       tioca_kern->ca_gart = page_address(tmp);
+       tioca_kern->ca_gart_coretalk_addr =
+           PHYS_TO_TIODMA(virt_to_phys(tioca_kern->ca_gart));
+
+       /*
+        * Compute PCI/AGP convenience fields 
+        */
+
+       offset = CA_PCI32_MAPPED_BASE - CA_APERATURE_BASE;
+       tioca_kern->ca_pciap_base = CA_PCI32_MAPPED_BASE;
+       tioca_kern->ca_pciap_size = CA_PCI32_MAPPED_SIZE;
+       tioca_kern->ca_pcigart_start = offset / tioca_kern->ca_ap_pagesize;
+       tioca_kern->ca_pcigart_base =
+           tioca_kern->ca_gart_coretalk_addr + offset;
+       tioca_kern->ca_pcigart =
+           &tioca_kern->ca_gart[tioca_kern->ca_pcigart_start];
+       tioca_kern->ca_pcigart_entries =
+           tioca_kern->ca_pciap_size / tioca_kern->ca_ap_pagesize;
+       tioca_kern->ca_pcigart_pagemap =
+           kcalloc(1, tioca_kern->ca_pcigart_entries / 8, GFP_KERNEL);
+       if (!tioca_kern->ca_pcigart_pagemap) {
+               free_pages((unsigned long)tioca_kern->ca_gart,
+                          get_order(tioca_kern->ca_gart_size));
+               return -1;
+       }
+
+       offset = CA_AGP_MAPPED_BASE - CA_APERATURE_BASE;
+       tioca_kern->ca_gfxap_base = CA_AGP_MAPPED_BASE;
+       tioca_kern->ca_gfxap_size = CA_AGP_MAPPED_SIZE;
+       tioca_kern->ca_gfxgart_start = offset / tioca_kern->ca_ap_pagesize;
+       tioca_kern->ca_gfxgart_base =
+           tioca_kern->ca_gart_coretalk_addr + offset;
+       tioca_kern->ca_gfxgart =
+           &tioca_kern->ca_gart[tioca_kern->ca_gfxgart_start];
+       tioca_kern->ca_gfxgart_entries =
+           tioca_kern->ca_gfxap_size / tioca_kern->ca_ap_pagesize;
+
+       /*
+        * various control settings:
+        *      use agp op-combining
+        *      use GET semantics to fetch memory
+        *      participate in coherency domain
+        *      DISABLE GART PREFETCHING due to hw bug tracked in SGI PV930029
+        */
+
+       ca_base->ca_control1 |= CA_AGPDMA_OP_ENB_COMBDELAY;     /* PV895469 ? */
+       ca_base->ca_control2 &= ~(CA_GART_MEM_PARAM);
+       ca_base->ca_control2 |= (0x2ull << CA_GART_MEM_PARAM_SHFT);
+       tioca_kern->ca_gart_iscoherent = 1;
+       ca_base->ca_control2 &=
+           ~(CA_GART_WR_PREFETCH_ENB | CA_GART_RD_PREFETCH_ENB);
+
+       /*
+        * Unmask GART fetch error interrupts.  Clear residual errors first.
+        */
+
+       ca_base->ca_int_status_alias = CA_GART_FETCH_ERR;
+       ca_base->ca_mult_error_alias = CA_GART_FETCH_ERR;
+       ca_base->ca_int_mask &= ~CA_GART_FETCH_ERR;
+
+       /*
+        * Program the aperature and gart registers in TIOCA
+        */
+
+       ca_base->ca_gart_aperature = ap_reg;
+       ca_base->ca_gart_ptr_table = tioca_kern->ca_gart_coretalk_addr | 1;
+
+       return 0;
+}
+
+/**
+ * tioca_fastwrite_enable - enable AGP FW for a tioca and its functions
+ * @tioca_kernel: structure representing the CA
+ *
+ * Given a CA, scan all attached functions making sure they all support
+ * FastWrite.  If so, enable FastWrite for all functions and the CA itself.
+ */
+
+void
+tioca_fastwrite_enable(struct tioca_kernel *tioca_kern)
+{
+       int cap_ptr;
+       uint64_t ca_control1;
+       uint32_t reg;
+       struct tioca *tioca_base;
+       struct pci_dev *pdev;
+       struct tioca_common *common;
+
+       common = tioca_kern->ca_common;
+
+       /*
+        * Scan all vga controllers on this bus making sure they all
+        * suport FW.  If not, return.
+        */
+
+       list_for_each_entry(pdev, tioca_kern->ca_devices, bus_list) {
+               if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8))
+                       continue;
+
+               cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+               if (!cap_ptr)
+                       return; /* no AGP CAP means no FW */
+
+               pci_read_config_dword(pdev, cap_ptr + PCI_AGP_STATUS, &reg);
+               if (!(reg & PCI_AGP_STATUS_FW))
+                       return; /* function doesn't support FW */
+       }
+
+       /*
+        * Set fw for all vga fn's
+        */
+
+       list_for_each_entry(pdev, tioca_kern->ca_devices, bus_list) {
+               if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8))
+                       continue;
+
+               cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+               pci_read_config_dword(pdev, cap_ptr + PCI_AGP_COMMAND, &reg);
+               reg |= PCI_AGP_COMMAND_FW;
+               pci_write_config_dword(pdev, cap_ptr + PCI_AGP_COMMAND, reg);
+       }
+
+       /*
+        * Set ca's fw to match
+        */
+
+       tioca_base = (struct tioca *)common->ca_common.bs_base;
+       ca_control1 = tioca_base->ca_control1;
+       ca_control1 |= CA_AGP_FW_ENABLE;
+       tioca_base->ca_control1 = ca_control1;
+}
+
+EXPORT_SYMBOL(tioca_fastwrite_enable); /* used by agp-sgi */
+
+/**
+ * tioca_dma_d64 - create a DMA mapping using 64-bit direct mode
+ * @paddr: system physical address
+ *
+ * Map @paddr into 64-bit CA bus space.  No device context is necessary.
+ * Bits 53:0 come from the coretalk address.  We just need to mask in the
+ * following optional bits of the 64-bit pci address:
+ *
+ * 63:60 - Coretalk Packet Type -  0x1 for Mem Get/Put (coherent)
+ *                                 0x2 for PIO (non-coherent)
+ *                                 We will always use 0x1
+ * 55:55 - Swap bytes             Currently unused
+ */
+static uint64_t
+tioca_dma_d64(unsigned long paddr)
+{
+       dma_addr_t bus_addr;
+
+       bus_addr = PHYS_TO_TIODMA(paddr);
+
+       BUG_ON(!bus_addr);
+       BUG_ON(bus_addr >> 54);
+
+       /* Set upper nibble to Cache Coherent Memory op */
+       bus_addr |= (1UL << 60);
+
+       return bus_addr;
+}
+
+/**
+ * tioca_dma_d48 - create a DMA mapping using 48-bit direct mode
+ * @pdev: linux pci_dev representing the function
+ * @paddr: system physical address
+ *
+ * Map @paddr into 64-bit bus space of the CA associated with @pcidev_info.
+ *
+ * The CA agp 48 bit direct address falls out as follows:
+ *
+ * When direct mapping AGP addresses, the 48 bit AGP address is
+ * constructed as follows:
+ *
+ * [47:40] - Low 8 bits of the page Node ID extracted from coretalk
+ *              address [47:40].  The upper 8 node bits are fixed
+ *              and come from the xxx register bits [5:0]
+ * [39:38] - Chiplet ID extracted from coretalk address [39:38]
+ * [37:00] - node offset extracted from coretalk address [37:00]
+ * 
+ * Since the node id in general will be non-zero, and the chiplet id
+ * will always be non-zero, it follows that the device must support
+ * a dma mask of at least 0xffffffffff (40 bits) to target node 0
+ * and in general should be 0xffffffffffff (48 bits) to target nodes
+ * up to 255.  Nodes above 255 need the support of the xxx register,
+ * and so a given CA can only directly target nodes in the range
+ * xxx - xxx+255.
+ */
+static uint64_t
+tioca_dma_d48(struct pci_dev *pdev, uint64_t paddr)
+{
+       struct tioca_common *tioca_common;
+       struct tioca *ca_base;
+       uint64_t ct_addr;
+       dma_addr_t bus_addr;
+       uint32_t node_upper;
+       uint64_t agp_dma_extn;
+       struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(pdev);
+
+       tioca_common = (struct tioca_common *)pcidev_info->pdi_pcibus_info;
+       ca_base = (struct tioca *)tioca_common->ca_common.bs_base;
+
+       ct_addr = PHYS_TO_TIODMA(paddr);
+       if (!ct_addr)
+               return 0;
+
+       bus_addr = (dma_addr_t) (ct_addr & 0xffffffffffff);
+       node_upper = ct_addr >> 48;
+
+       if (node_upper > 64) {
+               printk(KERN_ERR "%s:  coretalk addr 0x%p node id out "
+                      "of range\n", __FUNCTION__, (void *)ct_addr);
+               return 0;
+       }
+
+       agp_dma_extn = ca_base->ca_agp_dma_addr_extn;
+       if (node_upper != (agp_dma_extn >> CA_AGP_DMA_NODE_ID_SHFT)) {
+               printk(KERN_ERR "%s:  coretalk upper node (%u) "
+                      "mismatch with ca_agp_dma_addr_extn (%lu)\n",
+                      __FUNCTION__,
+                      node_upper, (agp_dma_extn >> CA_AGP_DMA_NODE_ID_SHFT));
+               return 0;
+       }
+
+       return bus_addr;
+}
+
+/**
+ * tioca_dma_mapped - create a DMA mapping using a CA GART 
+ * @pdev: linux pci_dev representing the function
+ * @paddr: host physical address to map
+ * @req_size: len (bytes) to map
+ *
+ * Map @paddr into CA address space using the GART mechanism.  The mapped
+ * dma_addr_t is guarenteed to be contiguous in CA bus space.
+ */
+static dma_addr_t
+tioca_dma_mapped(struct pci_dev *pdev, uint64_t paddr, size_t req_size)
+{
+       int i, ps, ps_shift, entry, entries, mapsize, last_entry;
+       uint64_t xio_addr, end_xio_addr;
+       struct tioca_common *tioca_common;
+       struct tioca_kernel *tioca_kern;
+       dma_addr_t bus_addr = 0;
+       struct tioca_dmamap *ca_dmamap;
+       void *map;
+       unsigned long flags;
+       struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(pdev);;
+
+       tioca_common = (struct tioca_common *)pcidev_info->pdi_pcibus_info;
+       tioca_kern = (struct tioca_kernel *)tioca_common->ca_kernel_private;
+
+       xio_addr = PHYS_TO_TIODMA(paddr);
+       if (!xio_addr)
+               return 0;
+
+       spin_lock_irqsave(&tioca_kern->ca_lock, flags);
+
+       /*
+        * allocate a map struct
+        */
+
+       ca_dmamap = kcalloc(1, sizeof(struct tioca_dmamap), GFP_ATOMIC);
+       if (!ca_dmamap)
+               goto map_return;
+
+       /*
+        * Locate free entries that can hold req_size.  Account for
+        * unaligned start/length when allocating.
+        */
+
+       ps = tioca_kern->ca_ap_pagesize;        /* will be power of 2 */
+       ps_shift = ffs(ps) - 1;
+       end_xio_addr = xio_addr + req_size - 1;
+
+       entries = (end_xio_addr >> ps_shift) - (xio_addr >> ps_shift) + 1;
+
+       map = tioca_kern->ca_pcigart_pagemap;
+       mapsize = tioca_kern->ca_pcigart_entries;
+
+       entry = find_first_zero_bit(map, mapsize);
+       while (entry < mapsize) {
+               last_entry = find_next_bit(map, mapsize, entry);
+
+               if (last_entry - entry >= entries)
+                       break;
+
+               entry = find_next_zero_bit(map, mapsize, last_entry);
+       }
+
+       if (entry > mapsize)
+               goto map_return;
+
+       for (i = 0; i < entries; i++)
+               set_bit(entry + i, map);
+
+       bus_addr = tioca_kern->ca_pciap_base + (entry * ps);
+
+       ca_dmamap->cad_dma_addr = bus_addr;
+       ca_dmamap->cad_gart_size = entries;
+       ca_dmamap->cad_gart_entry = entry;
+       list_add(&ca_dmamap->cad_list, &tioca_kern->ca_dmamaps);
+
+       if (xio_addr % ps) {
+               tioca_kern->ca_pcigart[entry] = tioca_paddr_to_gart(xio_addr);
+               bus_addr += xio_addr & (ps - 1);
+               xio_addr &= ~(ps - 1);
+               xio_addr += ps;
+               entry++;
+       }
+
+       while (xio_addr < end_xio_addr) {
+               tioca_kern->ca_pcigart[entry] = tioca_paddr_to_gart(xio_addr);
+               xio_addr += ps;
+               entry++;
+       }
+
+       tioca_tlbflush(tioca_kern);
+
+map_return:
+       spin_unlock_irqrestore(&tioca_kern->ca_lock, flags);
+       return bus_addr;
+}
+
+/**
+ * tioca_dma_unmap - release CA mapping resources
+ * @pdev: linux pci_dev representing the function
+ * @bus_addr: bus address returned by an earlier tioca_dma_map
+ * @dir: mapping direction (unused)
+ *
+ * Locate mapping resources associated with @bus_addr and release them.
+ * For mappings created using the direct modes (64 or 48) there are no
+ * resources to release.
+ */
+void
+tioca_dma_unmap(struct pci_dev *pdev, dma_addr_t bus_addr, int dir)
+{
+       int i, entry;
+       struct tioca_common *tioca_common;
+       struct tioca_kernel *tioca_kern;
+       struct tioca_dmamap *map;
+       struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(pdev);
+       unsigned long flags;
+
+       tioca_common = (struct tioca_common *)pcidev_info->pdi_pcibus_info;
+       tioca_kern = (struct tioca_kernel *)tioca_common->ca_kernel_private;
+
+       /* return straight away if this isn't be a mapped address */
+
+       if (bus_addr < tioca_kern->ca_pciap_base ||
+           bus_addr >= (tioca_kern->ca_pciap_base + tioca_kern->ca_pciap_size))
+               return;
+
+       spin_lock_irqsave(&tioca_kern->ca_lock, flags);
+
+       list_for_each_entry(map, &tioca_kern->ca_dmamaps, cad_list)
+           if (map->cad_dma_addr == bus_addr)
+               break;
+
+       BUG_ON(map == NULL);
+
+       entry = map->cad_gart_entry;
+
+       for (i = 0; i < map->cad_gart_size; i++, entry++) {
+               clear_bit(entry, tioca_kern->ca_pcigart_pagemap);
+               tioca_kern->ca_pcigart[entry] = 0;
+       }
+       tioca_tlbflush(tioca_kern);
+
+       list_del(&map->cad_list);
+       spin_unlock_irqrestore(&tioca_kern->ca_lock, flags);
+       kfree(map);
+}
+
+/**
+ * tioca_dma_map - map pages for PCI DMA
+ * @pdev: linux pci_dev representing the function
+ * @paddr: host physical address to map
+ * @byte_count: bytes to map
+ *
+ * This is the main wrapper for mapping host physical pages to CA PCI space.
+ * The mapping mode used is based on the devices dma_mask.  As a last resort
+ * use the GART mapped mode.
+ */
+uint64_t
+tioca_dma_map(struct pci_dev *pdev, uint64_t paddr, size_t byte_count)
+{
+       uint64_t mapaddr;
+
+       /*
+        * If card is 64 or 48 bit addresable, use a direct mapping.  32
+        * bit direct is so restrictive w.r.t. where the memory resides that
+        * we don't use it even though CA has some support.
+        */
+
+       if (pdev->dma_mask == ~0UL)
+               mapaddr = tioca_dma_d64(paddr);
+       else if (pdev->dma_mask == 0xffffffffffffUL)
+               mapaddr = tioca_dma_d48(pdev, paddr);
+       else
+               mapaddr = 0;
+
+       /* Last resort ... use PCI portion of CA GART */
+
+       if (mapaddr == 0)
+               mapaddr = tioca_dma_mapped(pdev, paddr, byte_count);
+
+       return mapaddr;
+}
+
+/**
+ * tioca_error_intr_handler - SGI TIO CA error interrupt handler
+ * @irq: unused
+ * @arg: pointer to tioca_common struct for the given CA
+ * @pt: unused
+ *
+ * Handle a CA error interrupt.  Simply a wrapper around a SAL call which
+ * defers processing to the SGI prom.
+ */
+static irqreturn_t
+tioca_error_intr_handler(int irq, void *arg, struct pt_regs *pt)
+{
+       struct tioca_common *soft = arg;
+       struct ia64_sal_retval ret_stuff;
+       uint64_t segment;
+       uint64_t busnum;
+       ret_stuff.status = 0;
+       ret_stuff.v0 = 0;
+
+       segment = 0;
+       busnum = soft->ca_common.bs_persist_busnum;
+
+       SAL_CALL_NOLOCK(ret_stuff,
+                       (u64) SN_SAL_IOIF_ERROR_INTERRUPT,
+                       segment, busnum, 0, 0, 0, 0, 0);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * tioca_bus_fixup - perform final PCI fixup for a TIO CA bus
+ * @prom_bussoft: Common prom/kernel struct representing the bus
+ *
+ * Replicates the tioca_common pointed to by @prom_bussoft in kernel
+ * space.  Allocates and initializes a kernel-only area for a given CA,
+ * and sets up an irq for handling CA error interrupts.
+ *
+ * On successful setup, returns the kernel version of tioca_common back to
+ * the caller.
+ */
+void *
+tioca_bus_fixup(struct pcibus_bussoft *prom_bussoft)
+{
+       struct tioca_common *tioca_common;
+       struct tioca_kernel *tioca_kern;
+       struct pci_bus *bus;
+
+       /* sanity check prom rev */
+
+       if (sn_sal_rev_major() < 4 ||
+           (sn_sal_rev_major() == 4 && sn_sal_rev_minor() < 6)) {
+               printk
+                   (KERN_ERR "%s:  SGI prom rev 4.06 or greater required "
+                    "for tioca support\n", __FUNCTION__);
+               return NULL;
+       }
+
+       /*
+        * Allocate kernel bus soft and copy from prom.
+        */
+
+       tioca_common = kcalloc(1, sizeof(struct tioca_common), GFP_KERNEL);
+       if (!tioca_common)
+               return NULL;
+
+       memcpy(tioca_common, prom_bussoft, sizeof(struct tioca_common));
+       tioca_common->ca_common.bs_base |= __IA64_UNCACHED_OFFSET;
+
+       /* init kernel-private area */
+
+       tioca_kern = kcalloc(1, sizeof(struct tioca_kernel), GFP_KERNEL);
+       if (!tioca_kern) {
+               kfree(tioca_common);
+               return NULL;
+       }
+
+       tioca_kern->ca_common = tioca_common;
+       spin_lock_init(&tioca_kern->ca_lock);
+       INIT_LIST_HEAD(&tioca_kern->ca_dmamaps);
+       tioca_kern->ca_closest_node =
+           nasid_to_cnodeid(tioca_common->ca_closest_nasid);
+       tioca_common->ca_kernel_private = (uint64_t) tioca_kern;
+
+       bus = pci_find_bus(0, tioca_common->ca_common.bs_persist_busnum);
+       BUG_ON(!bus);
+       tioca_kern->ca_devices = &bus->devices;
+
+       /* init GART */
+
+       if (tioca_gart_init(tioca_kern) < 0) {
+               kfree(tioca_kern);
+               kfree(tioca_common);
+               return NULL;
+       }
+
+       tioca_gart_found++;
+       list_add(&tioca_kern->ca_list, &tioca_list);
+
+       if (request_irq(SGI_TIOCA_ERROR,
+                       tioca_error_intr_handler,
+                       SA_SHIRQ, "TIOCA error", (void *)tioca_common))
+               printk(KERN_WARNING
+                      "%s:  Unable to get irq %d.  "
+                      "Error interrupts won't be routed for TIOCA bus %d\n",
+                      __FUNCTION__, SGI_TIOCA_ERROR,
+                      (int)tioca_common->ca_common.bs_persist_busnum);
+
+       return tioca_common;
+}
+
+static struct sn_pcibus_provider tioca_pci_interfaces = {
+       .dma_map = tioca_dma_map,
+       .dma_map_consistent = tioca_dma_map,
+       .dma_unmap = tioca_dma_unmap,
+       .bus_fixup = tioca_bus_fixup,
+};
+
+/**
+ * tioca_init_provider - init SN PCI provider ops for TIO CA
+ */
+int
+tioca_init_provider(void)
+{
+       sn_pci_provider[PCIIO_ASIC_TYPE_TIOCA] = &tioca_pci_interfaces;
+       return 0;
+}
diff --git a/arch/m68knommu/platform/68328/head-pilot.S b/arch/m68knommu/platform/68328/head-pilot.S
new file mode 100644 (file)
index 0000000..c46775f
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * linux/arch/m68knommu/platform/68328/head-rom.S
+ * - A startup file for the MC68328
+ *
+ * Copyright (C) 1998  D. Jeff Dionne <jeff@ryeham.ee.ryerson.ca>,
+ *                     Kenneth Albanowski <kjahds@kjahds.com>,
+ *                     The Silver Hammer Group, Ltd.
+ *
+ * (c) 1995, Dionne & Associates
+ * (c) 1995, DKG Display Tech.
+ */
+
+#define ASSEMBLY
+
+#define IMMED #
+#define        DBG_PUTC(x)     moveb IMMED x, 0xfffff907
+
+#include <linux/config.h>
+
+.global _stext
+.global _start
+
+.global _rambase
+.global __ramvec
+.global _ramvec
+.global _ramstart
+.global _ramend
+
+.global penguin_bits
+
+#ifdef CONFIG_PILOT
+
+#define IMR 0xFFFFF304
+
+       .data
+       .align 16
+
+penguin_bits:  
+#include "bootlogo.rh"
+
+#endif
+
+/*****************************************************************************/
+
+.data
+
+/*
+ *      Set up the usable of RAM stuff. Size of RAM is determined then
+ *      an initial stack set up at the end.
+ */
+.align 4
+_ramvec:
+.long   0
+_rambase:
+.long   0
+_ramstart:
+.long   0
+_ramend:
+.long   0
+
+.text
+       
+_start:
+_stext:
+
+
+#ifdef CONFIG_M68328
+
+#ifdef CONFIG_PILOT
+       .byte 0x4e, 0xfa, 0x00, 0x0a /* Jmp +X bytes */
+       .byte 'b', 'o', 'o', 't'
+       .word 10000
+
+       nop
+#endif
+
+       moveq   #0, %d0
+       movew   %d0, 0xfffff618 /* Watchdog off */
+       movel   #0x00011f07, 0xfffff114 /* CS A1 Mask */
+
+       movew   #0x0800, 0xfffff906 /* Ignore CTS */
+       movew   #0x010b, 0xfffff902 /* BAUD to 9600 */
+
+       movew   #0x2410, 0xfffff200 /* PLLCR */
+       movew   #0x123, 0xfffff202 /* PLLFSR */
+
+#ifdef CONFIG_PILOT
+       moveb   #0, 0xfffffA27 /* LCKCON */
+       movel   #_start, 0xfffffA00 /* LSSA */
+       moveb   #0xa, 0xfffffA05 /* LVPW */
+       movew   #0x9f, 0xFFFFFa08 /* LXMAX */
+       movew   #0x9f, 0xFFFFFa0a /* LYMAX */
+       moveb   #9, 0xfffffa29 /* LBAR */
+       moveb   #0, 0xfffffa25 /* LPXCD */
+       moveb   #0x04, 0xFFFFFa20 /* LPICF */
+       moveb   #0x58, 0xfffffA27 /* LCKCON */
+       moveb   #0x85, 0xfffff429 /* PFDATA */
+       moveb   #0xd8, 0xfffffA27 /* LCKCON */
+       moveb   #0xc5, 0xfffff429 /* PFDATA */
+       moveb   #0xd5, 0xfffff429 /* PFDATA */
+
+       moveal  #0x00100000, %a3
+       moveal  #0x100ffc00, %a4
+#endif /* CONFIG_PILOT */
+
+#endif /* CONFIG_M68328 */
+
+       movew   #0x2700, %sr
+       lea     %a4@(-4), %sp
+
+       DBG_PUTC('\r')
+       DBG_PUTC('\n')
+       DBG_PUTC('A')
+
+       moveq   #0,%d0
+       movew   #16384, %d0  /* PLL settle wait loop */
+L0:
+       subw    #1, %d0
+       bne     L0
+
+       DBG_PUTC('B')
+
+       /* Copy command line from beginning of RAM (+16) to end of bss */
+       movel   #__ramvec, %d7
+       addl    #16, %d7
+       moveal  %d7, %a0
+       moveal  #_ebss, %a1
+       lea     %a1@(512), %a2
+
+       DBG_PUTC('C')
+
+       /* Copy %a0 to %a1 until %a1 == %a2 */
+L2:
+       movel   %a0@+, %d0
+       movel   %d0, %a1@+
+       cmpal   %a1, %a2
+       bhi     L2
+
+       /* Copy data+init segment from ROM to RAM */
+       moveal  #_etext, %a0
+       moveal  #_sdata, %a1
+       moveal  #__init_end, %a2
+
+       DBG_PUTC('D')
+
+       /* Copy %a0 to %a1 until %a1 == %a2 */
+LD1:
+       movel   %a0@+, %d0
+       movel   %d0, %a1@+
+       cmpal   %a1, %a2
+       bhi     LD1
+
+       DBG_PUTC('E')
+
+       moveal  #_sbss, %a0
+       moveal  #_ebss, %a1
+
+       /* Copy 0 to %a0 until %a0 == %a1 */
+L1:
+       movel   #0, %a0@+
+       cmpal   %a0, %a1
+       bhi     L1
+
+       DBG_PUTC('F')
+
+       /* Copy command line from end of bss to command line */
+       moveal  #_ebss, %a0
+       moveal  #command_line, %a1
+       lea     %a1@(512), %a2
+
+       DBG_PUTC('G')
+
+       /* Copy %a0 to %a1 until %a1 == %a2 */
+L3:
+       movel   %a0@+, %d0
+       movel   %d0, %a1@+
+       cmpal   %a1, %a2
+       bhi     L3
+
+       movel   #_sdata, %d0    
+       movel   %d0, _rambase   
+       movel   #_ebss, %d0
+       movel   %d0, _ramstart
+
+       movel   %a4, %d0
+       subl    #4096, %d0      /* Reserve 4K of stack */
+       moveq   #79, %d7
+       movel   %d0, _ramend
+
+       movel   %a3, %d0
+       movel   %d0, rom_length
+
+       pea     0
+       pea     env
+       pea     %sp@(4)
+       pea     0
+
+       DBG_PUTC('H')
+
+#ifdef CONFIG_PILOT
+       movel   #penguin_bits, 0xFFFFFA00
+       moveb   #10, 0xFFFFFA05
+       movew   #160, 0xFFFFFA08
+       movew   #160, 0xFFFFFA0A
+#endif /* CONFIG_PILOT */
+
+       DBG_PUTC('I')
+
+       lea     init_thread_union, %a0
+       lea     0x2000(%a0), %sp
+
+       DBG_PUTC('J')
+       DBG_PUTC('\r')
+       DBG_PUTC('\n')
+
+       jsr     start_kernel
+_exit:
+
+       jmp     _exit
+
+
+       .data
+env:
+       .long   0
diff --git a/arch/m68knommu/platform/68328/head-ram.S b/arch/m68knommu/platform/68328/head-ram.S
new file mode 100644 (file)
index 0000000..6bdc9bc
--- /dev/null
@@ -0,0 +1,171 @@
+#include <linux/config.h>
+
+       .global __main
+       .global __ram_start
+       .global __ram_end
+       .global __rom_start
+       .global __rom_end
+
+        .global _rambase
+        .global _ramstart
+       
+       .global splash_bits
+       .global _start
+       .global _stext
+
+#define DEBUG
+#define ROM_OFFSET 0x10C00000
+#define STACK_GAURD 0x10
+
+       .text
+       
+_start:
+_stext:
+       movew   #0x2700, %sr            /* Exceptions off! */
+
+#if 0
+       /* Init chip registers.  uCsimm specific */
+       moveb   #0x00,   0xfffffb0b     /* Watchdog off */
+       moveb   #0x10,   0xfffff000     /* SCR */
+
+       movew   #0x2400, 0xfffff200     /* PLLCR */
+       movew   #0x0123, 0xfffff202     /* PLLFSR */
+
+       moveb   #0x00,   0xfffff40b     /* enable chip select */
+       moveb   #0x00,   0xfffff423     /* enable /DWE */
+       moveb   #0x08,   0xfffffd0d     /* disable hardmap */
+       moveb   #0x07,   0xfffffd0e     /* level 7 interrupt clear */
+
+       movew   #0x8600, 0xfffff100     /* FLASH at 0x10c00000 */
+       movew   #0x018b, 0xfffff110     /* 2Meg, enable, 0ws */
+
+       movew   #0x8f00, 0xfffffc00     /* DRAM configuration */
+       movew   #0x9667, 0xfffffc02     /* DRAM control */
+       movew   #0x0000, 0xfffff106     /* DRAM at 0x00000000 */
+       movew   #0x068f, 0xfffff116     /* 8Meg, enable, 0ws */
+
+       moveb   #0x40,   0xfffff300     /* IVR */
+       movel   #0x007FFFFF, %d0        /* IMR */
+       movel   %d0,     0xfffff304
+
+       moveb   0xfffff42b, %d0
+       andb    #0xe0,   %d0
+       moveb   %d0,     0xfffff42b
+
+       moveb   #0x08,   0xfffff907     /* Ignore CTS */
+       movew   #0x010b, 0xfffff902     /* BAUD to 9600 */
+       movew   #0xe100, 0xfffff900     /* enable */
+#endif
+
+       movew   #16384, %d0  /* PLL settle wait loop */
+L0:
+       subw    #1, %d0
+       bne     L0
+#ifdef DEBUG
+       moveq   #70, %d7                /* 'F' */
+       moveb   %d7,0xfffff907          /* No absolute addresses */
+pclp1:
+       movew   0xfffff906, %d7
+       andw    #0x2000, %d7
+       beq     pclp1
+#endif /* DEBUG */
+
+#ifdef CONFIG_RELOCATE
+       /* Copy me to RAM */
+       moveal  #__rom_start, %a0
+       moveal  #__ram_start, %a1
+       moveal  #_edata, %a2
+
+       /* Copy %a0 to %a1 until %a1 == %a2 */
+LD1:
+       movel   %a0@+, %d0
+       movel   %d0, %a1@+
+       cmpal   %a1, %a2
+       bhi     LD1
+       
+#ifdef DEBUG
+       moveq   #74, %d7                /* 'J' */
+       moveb   %d7,0xfffff907          /* No absolute addresses */
+pclp2:
+       movew   0xfffff906, %d7
+       andw    #0x2000, %d7
+       beq     pclp2
+#endif /* DEBUG */
+       /* jump into the RAM copy */
+       jmp     ram_jump
+ram_jump:
+
+#endif /* CONFIG_RELOCATE */
+
+#ifdef DEBUG
+       moveq   #82, %d7                /* 'R' */
+       moveb   %d7,0xfffff907          /* No absolute addresses */
+pclp3:
+       movew   0xfffff906, %d7
+       andw    #0x2000, %d7
+       beq     pclp3
+#endif /* DEBUG */
+       moveal  #0x007ffff0, %ssp
+       moveal  #_sbss, %a0
+       moveal  #_ebss, %a1
+
+       /* Copy 0 to %a0 until %a0 >= %a1 */
+L1:
+       movel   #0, %a0@+
+       cmpal   %a0, %a1
+       bhi     L1
+
+#ifdef DEBUG
+       moveq   #67, %d7                /* 'C' */
+       jsr     putc
+#endif /* DEBUG */
+
+       pea     0
+       pea     env
+       pea     %sp@(4)
+       pea     0
+
+#ifdef DEBUG
+       moveq   #70, %d7                /* 'F' */
+       jsr     putc
+#endif /* DEBUG */
+
+lp:
+       jsr     start_kernel
+        jmp lp
+_exit:
+
+       jmp     _exit
+
+__main:
+       /* nothing */
+       rts
+
+#ifdef DEBUG
+putc:
+       moveb   %d7,0xfffff907
+pclp:
+       movew   0xfffff906, %d7
+       andw    #0x2000, %d7
+       beq     pclp
+       rts
+#endif /* DEBUG */
+
+       .data
+
+/*
+ *      Set up the usable of RAM stuff. Size of RAM is determined then
+ *      an initial stack set up at the end.
+ */
+.align 4
+_ramvec:
+.long   0
+_rambase:
+.long   0
+_ramstart:
+.long   0
+_ramend:
+.long   0
+
+env:
+       .long   0
diff --git a/arch/m68knommu/platform/68328/head-rom.S b/arch/m68knommu/platform/68328/head-rom.S
new file mode 100644 (file)
index 0000000..2b448a2
--- /dev/null
@@ -0,0 +1,109 @@
+#include <linux/config.h>
+       
+       .global _start
+       .global _stext
+
+       .global _rambase
+       .global _ramvec
+       .global _ramstart
+       .global _ramend
+
+#ifdef CONFIG_INIT_LCD
+       .global splash_bits
+#endif
+
+       .data
+
+/*
+ *      Set up the usable of RAM stuff. Size of RAM is determined then
+ *      an initial stack set up at the end.
+ */
+.align 4
+_ramvec:
+.long   0
+_rambase:
+.long   0
+_ramstart:
+.long   0
+_ramend:
+.long   0
+
+#ifdef CONFIG_INIT_LCD
+splash_bits:
+#include "bootlogo.rh"
+#endif
+       
+       .text
+_start:
+_stext:        movew   #0x2700,%sr
+#ifdef CONFIG_INIT_LCD
+       movel   #splash_bits, 0xfffffA00 /* LSSA */
+       moveb   #0x28,   0xfffffA05     /* LVPW */
+       movew   #0x280,  0xFFFFFa08     /* LXMAX */
+       movew   #0x1df,  0xFFFFFa0a     /* LYMAX */
+       moveb   #0,      0xfffffa29     /* LBAR */
+       moveb   #0,      0xfffffa25     /* LPXCD */
+       moveb   #0x08,   0xFFFFFa20     /* LPICF */
+       moveb   #0x01,   0xFFFFFA21     /* -ve pol */
+       moveb   #0x81,   0xfffffA27     /* LCKCON */
+       movew   #0xff00, 0xfffff412     /* LCD pins */
+#endif
+       moveal  #__ramend-CONFIG_MEMORY_RESERVE*0x100000 - 0x10, %sp
+       movew   #32767, %d0  /* PLL settle wait loop */
+1:     subq    #1, %d0
+       bne     1b
+
+       /* Copy data segment from ROM to RAM */
+       moveal  #_etext, %a0
+       moveal  #_sdata, %a1
+       moveal  #_edata, %a2
+
+       /* Copy %a0 to %a1 until %a1 == %a2 */
+1:     movel   %a0@+, %a1@+
+       cmpal   %a1, %a2
+       bhi     1b
+
+       moveal  #_sbss, %a0
+       moveal  #_ebss, %a1
+       /* Copy 0 to %a0 until %a0 == %a1 */
+       
+1:
+       clrl    %a0@+
+       cmpal   %a0, %a1
+       bhi     1b
+
+        movel   #_sdata, %d0    
+        movel   %d0,    _rambase        
+        movel   #_ebss,  %d0
+        movel   %d0,    _ramstart
+       movel   #__ramend-CONFIG_MEMORY_RESERVE*0x100000, %d0
+       movel   %d0,    _ramend
+       movel   #__ramvec,      %d0
+       movel   %d0,    _ramvec
+       
+/*
+ * load the current task pointer and stack
+ */
+       lea     init_thread_union, %a0
+       lea     0x2000(%a0), %sp
+
+1:     jsr     start_kernel
+        bra 1b
+_exit:
+
+       jmp     _exit
+
+
+putc:
+       moveb   %d7,0xfffff907
+1:
+       movew   0xfffff906, %d7
+       andw    #0x2000, %d7
+       beq     1b
+       rts
+
+       .data
+env:
+       .long   0
+       .text
+
diff --git a/arch/ppc/boot/simple/misc-chestnut.c b/arch/ppc/boot/simple/misc-chestnut.c
new file mode 100644 (file)
index 0000000..0dce7f3
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * arch/ppc/boot/simple/misc-chestnut.c
+ *
+ * Setup for the IBM Chestnut (ibm-750fxgx_eval)
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (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/types.h>
+#include <asm/io.h>
+#include <asm/mv64x60_defs.h>
+#include <platforms/chestnut.h>
+
+/* Not in the kernel so won't include kernel.h to get its 'max' definition */
+#define max(a,b)       (((a) > (b)) ? (a) : (b))
+
+void
+mv64x60_board_init(void __iomem *old_base, void __iomem *new_base)
+{
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+       /*
+        * Change device bus 2 window so that bootoader can do I/O thru
+        * 8250/16550 UART that's mapped in that window.
+        */
+       out_le32(new_base + MV64x60_CPU2DEV_2_BASE, CHESTNUT_UART_BASE >> 16);
+       out_le32(new_base + MV64x60_CPU2DEV_2_SIZE, CHESTNUT_UART_SIZE >> 16);
+       __asm__ __volatile__("sync");
+#endif
+}
diff --git a/arch/ppc/boot/simple/misc-ev64260.c b/arch/ppc/boot/simple/misc-ev64260.c
new file mode 100644 (file)
index 0000000..52ece69
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * arch/ppc/boot/simple/misc-ev64260.c
+ *
+ * Host bridge init code for the Marvell/Galileo EV-64260-BP evaluation board
+ * with a GT64260 onboard.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2001 (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/types.h>
+#include <asm/reg.h>
+#include <asm/io.h>
+#include <asm/mv64x60_defs.h>
+#include <platforms/ev64260.h>
+
+#ifdef CONFIG_SERIAL_MPSC_CONSOLE
+extern u32 mv64x60_console_baud;
+extern u32 mv64x60_mpsc_clk_src;
+extern u32 mv64x60_mpsc_clk_freq;
+#endif
+
+void
+mv64x60_board_init(void __iomem *old_base, void __iomem *new_base)
+{
+       u32     p, v;
+
+       /* DINK doesn't enable 745x timebase, so enable here (Adrian Cox) */
+       p = mfspr(SPRN_PVR);
+       p >>= 16;
+
+       /* Reasonable SWAG at a 745x PVR value */
+       if (((p & 0xfff0) == 0x8000) && (p != 0x800c)) {
+               v = mfspr(SPRN_HID0);
+               v |= HID0_TBEN;
+               mtspr(SPRN_HID0, v);
+       }
+
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+       /*
+        * Change device bus 2 window so that bootoader can do I/O thru
+        * 8250/16550 UART that's mapped in that window.
+        */
+       out_le32(new_base + MV64x60_CPU2DEV_2_BASE, EV64260_UART_BASE >> 20);
+       out_le32(new_base + MV64x60_CPU2DEV_2_SIZE, EV64260_UART_END >> 20);
+       __asm__ __volatile__("sync");
+#elif defined(CONFIG_SERIAL_MPSC_CONSOLE)
+       mv64x60_console_baud = EV64260_DEFAULT_BAUD;
+       mv64x60_mpsc_clk_src = EV64260_MPSC_CLK_SRC;
+       mv64x60_mpsc_clk_freq = EV64260_MPSC_CLK_FREQ;
+#endif
+}
diff --git a/arch/ppc/boot/simple/misc-mv64x60.c b/arch/ppc/boot/simple/misc-mv64x60.c
new file mode 100644 (file)
index 0000000..7e88fc6
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * arch/ppc/boot/simple/misc-mv64x60.c
+ *
+ * Relocate bridge's register base and call board specific routine.
+ *
+ * Author: Mark A. Greer <source@mvista.com>
+ *
+ * 2005 (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/types.h>
+#include <asm/io.h>
+#include <asm/mv64x60_defs.h>
+
+extern struct bi_record *decompress_kernel(unsigned long load_addr,
+       int num_words, unsigned long cksum);
+
+void
+mv64x60_move_base(void __iomem *old_base, void __iomem *new_base)
+{
+       u32     bits, mask, b;
+
+       if (old_base != new_base) {
+#ifdef CONFIG_GT64260
+               bits = 12;
+               mask = 0x07000000;
+#else /* Must be mv64[34]60 */
+               bits = 16;
+               mask = 0x03000000;
+#endif
+               b = in_le32(old_base + MV64x60_INTERNAL_SPACE_DECODE);
+               b &= mask;
+               b |= ((u32)new_base >> (32 - bits));
+               out_le32(old_base + MV64x60_INTERNAL_SPACE_DECODE, b);
+
+               __asm__ __volatile__("sync");
+
+               /* Wait for change to happen (in accordance with the manual) */
+               while (in_le32(new_base + MV64x60_INTERNAL_SPACE_DECODE) != b);
+       }
+}
+
+void __attribute__ ((weak))
+mv64x60_board_init(void __iomem *old_base, void __iomem *new_base)
+{
+}
+
+void *
+load_kernel(unsigned long load_addr, int num_words, unsigned long cksum,
+               void *ign1, void *ign2)
+{
+       mv64x60_move_base((void __iomem *)CONFIG_MV64X60_BASE,
+               (void __iomem *)CONFIG_MV64X60_NEW_BASE);
+       mv64x60_board_init((void __iomem *)CONFIG_MV64X60_BASE,
+               (void __iomem *)CONFIG_MV64X60_NEW_BASE);
+       return decompress_kernel(load_addr, num_words, cksum);
+}
diff --git a/arch/ppc/boot/simple/misc-radstone_ppc7d.c b/arch/ppc/boot/simple/misc-radstone_ppc7d.c
new file mode 100644 (file)
index 0000000..569e0d4
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * arch/ppc/boot/simple/misc-radstone_ppc7d.c
+ *
+ * Misc data for Radstone PPC7D board.
+ *
+ * Author: James Chapman <jchapman@katalix.com>
+ */
+
+#include <linux/types.h>
+#include <platforms/radstone_ppc7d.h>
+
+#if defined(CONFIG_SERIAL_MPSC_CONSOLE)
+extern u32 mv64x60_console_baud;
+extern u32 mv64x60_mpsc_clk_src;
+extern u32 mv64x60_mpsc_clk_freq;
+#endif
+
+void
+mv64x60_board_init(void __iomem *old_base, void __iomem *new_base)
+{
+#if defined(CONFIG_SERIAL_MPSC_CONSOLE)
+       mv64x60_console_baud = PPC7D_DEFAULT_BAUD;
+       mv64x60_mpsc_clk_src = PPC7D_MPSC_CLK_SRC;
+       mv64x60_mpsc_clk_freq = PPC7D_MPSC_CLK_FREQ;
+#endif
+}
diff --git a/arch/ppc/boot/simple/openbios.c b/arch/ppc/boot/simple/openbios.c
new file mode 100644 (file)
index 0000000..c732b6d
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * arch/ppc/boot/simple/openbios.c
+ *
+ * 2005 (c) SYSGO AG - g.jaeger@sysgo.com
+ * 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.
+ *
+ * Derived from arch/ppc/boot/simple/pibs.c (from MontaVista)
+ */
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/string.h>
+#include <asm/ppcboot.h>
+#include <platforms/4xx/ebony.h>
+
+extern unsigned long decompress_kernel(unsigned long load_addr, int num_words,
+                                      unsigned long cksum);
+
+/* We need to make sure that this is before the images to ensure
+ * that it's in a mapped location. */
+bd_t hold_resid_buf __attribute__ ((__section__ (".data.boot")));
+bd_t *hold_residual = &hold_resid_buf;
+
+void *
+load_kernel(unsigned long load_addr, int num_words, unsigned long cksum,
+               void *ign1, void *ign2)
+{
+       decompress_kernel(load_addr, num_words, cksum);
+
+       /* simply copy the MAC addresses */
+       memcpy(hold_residual->bi_enetaddr,  (char *)EBONY_OPENBIOS_MAC_BASE, 6);
+       memcpy(hold_residual->bi_enet1addr, (char *)(EBONY_OPENBIOS_MAC_BASE+EBONY_OPENBIOS_MAC_OFFSET), 6);
+
+       return (void *)hold_residual;
+}
diff --git a/arch/ppc/kernel/fpu.S b/arch/ppc/kernel/fpu.S
new file mode 100644 (file)
index 0000000..6189b26
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ *  FPU support code, moved here from head.S so that it can be used
+ *  by chips which use other head-whatever.S files.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the 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/page.h>
+#include <asm/mmu.h>
+#include <asm/pgtable.h>
+#include <asm/cputable.h>
+#include <asm/cache.h>
+#include <asm/thread_info.h>
+#include <asm/ppc_asm.h>
+#include <asm/offsets.h>
+
+/*
+ * This task wants to use the FPU now.
+ * On UP, disable FP for the task which had the FPU previously,
+ * and save its floating-point registers in its thread_struct.
+ * Load up this task's FP registers from its thread_struct,
+ * enable the FPU for the current task and return to the task.
+ */
+       .globl  load_up_fpu
+load_up_fpu:
+       mfmsr   r5
+       ori     r5,r5,MSR_FP
+#ifdef CONFIG_PPC64BRIDGE
+       clrldi  r5,r5,1                 /* turn off 64-bit mode */
+#endif /* CONFIG_PPC64BRIDGE */
+       SYNC
+       MTMSRD(r5)                      /* enable use of fpu now */
+       isync
+/*
+ * For SMP, we don't do lazy FPU switching because it just gets too
+ * horrendously complex, especially when a task switches from one CPU
+ * to another.  Instead we call giveup_fpu in switch_to.
+ */
+#ifndef CONFIG_SMP
+       tophys(r6,0)                    /* get __pa constant */
+       addis   r3,r6,last_task_used_math@ha
+       lwz     r4,last_task_used_math@l(r3)
+       cmpwi   0,r4,0
+       beq     1f
+       add     r4,r4,r6
+       addi    r4,r4,THREAD            /* want last_task_used_math->thread */
+       SAVE_32FPRS(0, r4)
+       mffs    fr0
+       stfd    fr0,THREAD_FPSCR-4(r4)
+       lwz     r5,PT_REGS(r4)
+       add     r5,r5,r6
+       lwz     r4,_MSR-STACK_FRAME_OVERHEAD(r5)
+       li      r10,MSR_FP|MSR_FE0|MSR_FE1
+       andc    r4,r4,r10               /* disable FP for previous task */
+       stw     r4,_MSR-STACK_FRAME_OVERHEAD(r5)
+1:
+#endif /* CONFIG_SMP */
+       /* enable use of FP after return */
+       mfspr   r5,SPRN_SPRG3           /* current task's THREAD (phys) */
+       lwz     r4,THREAD_FPEXC_MODE(r5)
+       ori     r9,r9,MSR_FP            /* enable FP for current */
+       or      r9,r9,r4
+       lfd     fr0,THREAD_FPSCR-4(r5)
+       mtfsf   0xff,fr0
+       REST_32FPRS(0, r5)
+#ifndef CONFIG_SMP
+       subi    r4,r5,THREAD
+       sub     r4,r4,r6
+       stw     r4,last_task_used_math@l(r3)
+#endif /* CONFIG_SMP */
+       /* restore registers and return */
+       /* we haven't used ctr or xer or lr */
+       b       fast_exception_return
+
+/*
+ * FP unavailable trap from kernel - print a message, but let
+ * the task use FP in the kernel until it returns to user mode.
+ */
+       .globl  KernelFP
+KernelFP:
+       lwz     r3,_MSR(r1)
+       ori     r3,r3,MSR_FP
+       stw     r3,_MSR(r1)             /* enable use of FP after return */
+       lis     r3,86f@h
+       ori     r3,r3,86f@l
+       mr      r4,r2                   /* current */
+       lwz     r5,_NIP(r1)
+       bl      printk
+       b       ret_from_except
+86:    .string "floating point used in kernel (task=%p, pc=%x)\n"
+       .align  4,0
+
+/*
+ * giveup_fpu(tsk)
+ * Disable FP for the task given as the argument,
+ * and save the floating-point registers in its thread_struct.
+ * Enables the FPU for use in the kernel on return.
+ */
+       .globl  giveup_fpu
+giveup_fpu:
+       mfmsr   r5
+       ori     r5,r5,MSR_FP
+       SYNC_601
+       ISYNC_601
+       MTMSRD(r5)                      /* enable use of fpu now */
+       SYNC_601
+       isync
+       cmpwi   0,r3,0
+       beqlr-                          /* if no previous owner, done */
+       addi    r3,r3,THREAD            /* want THREAD of task */
+       lwz     r5,PT_REGS(r3)
+       cmpwi   0,r5,0
+       SAVE_32FPRS(0, r3)
+       mffs    fr0
+       stfd    fr0,THREAD_FPSCR-4(r3)
+       beq     1f
+       lwz     r4,_MSR-STACK_FRAME_OVERHEAD(r5)
+       li      r3,MSR_FP|MSR_FE0|MSR_FE1
+       andc    r4,r4,r3                /* disable FP for previous task */
+       stw     r4,_MSR-STACK_FRAME_OVERHEAD(r5)
+1:
+#ifndef CONFIG_SMP
+       li      r5,0
+       lis     r4,last_task_used_math@ha
+       stw     r5,last_task_used_math@l(r4)
+#endif /* CONFIG_SMP */
+       blr
diff --git a/arch/ppc/kernel/head_fsl_booke.S b/arch/ppc/kernel/head_fsl_booke.S
new file mode 100644 (file)
index 0000000..ce36e88
--- /dev/null
@@ -0,0 +1,1004 @@
+/*
+ * arch/ppc/kernel/head_fsl_booke.S
+ *
+ * Kernel execution entry point code.
+ *
+ *    Copyright (c) 1995-1996 Gary Thomas <gdt@linuxppc.org>
+ *      Initial PowerPC version.
+ *    Copyright (c) 1996 Cort Dougan <cort@cs.nmt.edu>
+ *      Rewritten for PReP
+ *    Copyright (c) 1996 Paul Mackerras <paulus@cs.anu.edu.au>
+ *      Low-level exception handers, MMU support, and rewrite.
+ *    Copyright (c) 1997 Dan Malek <dmalek@jlc.net>
+ *      PowerPC 8xx modifications.
+ *    Copyright (c) 1998-1999 TiVo, Inc.
+ *      PowerPC 403GCX modifications.
+ *    Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
+ *      PowerPC 403GCX/405GP modifications.
+ *    Copyright 2000 MontaVista Software Inc.
+ *     PPC405 modifications
+ *      PowerPC 403GCX/405GP modifications.
+ *     Author: MontaVista Software, Inc.
+ *             frank_rowand@mvista.com or source@mvista.com
+ *             debbie_chu@mvista.com
+ *    Copyright 2002-2004 MontaVista Software, Inc.
+ *      PowerPC 44x support, Matt Porter <mporter@kernel.crashing.org>
+ *    Copyright 2004 Freescale Semiconductor, Inc
+ *      PowerPC e500 modifications, 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/config.h>
+#include <linux/threads.h>
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/mmu.h>
+#include <asm/pgtable.h>
+#include <asm/cputable.h>
+#include <asm/thread_info.h>
+#include <asm/ppc_asm.h>
+#include <asm/offsets.h>
+#include "head_booke.h"
+
+/* As with the other PowerPC ports, it is expected that when code
+ * execution begins here, the following registers contain valid, yet
+ * optional, information:
+ *
+ *   r3 - Board info structure pointer (DRAM, frequency, MAC address, etc.)
+ *   r4 - Starting address of the init RAM disk
+ *   r5 - Ending address of the init RAM disk
+ *   r6 - Start of kernel command line string (e.g. "mem=128")
+ *   r7 - End of kernel command line string
+ *
+ */
+       .text
+_GLOBAL(_stext)
+_GLOBAL(_start)
+       /*
+        * Reserve a word at a fixed location to store the address
+        * of abatron_pteptrs
+        */
+       nop
+/*
+ * Save parameters we are passed
+ */
+       mr      r31,r3
+       mr      r30,r4
+       mr      r29,r5
+       mr      r28,r6
+       mr      r27,r7
+       li      r24,0           /* CPU number */
+
+/* We try to not make any assumptions about how the boot loader
+ * setup or used the TLBs.  We invalidate all mappings from the
+ * boot loader and load a single entry in TLB1[0] to map the
+ * first 16M of kernel memory.  Any boot info passed from the
+ * bootloader needs to live in this first 16M.
+ *
+ * Requirement on bootloader:
+ *  - The page we're executing in needs to reside in TLB1 and
+ *    have IPROT=1.  If not an invalidate broadcast could
+ *    evict the entry we're currently executing in.
+ *
+ *  r3 = Index of TLB1 were executing in
+ *  r4 = Current MSR[IS]
+ *  r5 = Index of TLB1 temp mapping
+ *
+ * Later in mapin_ram we will correctly map lowmem, and resize TLB1[0]
+ * if needed
+ */
+
+/* 1. Find the index of the entry we're executing in */
+       bl      invstr                          /* Find our address */
+invstr:        mflr    r6                              /* Make it accessible */
+       mfmsr   r7
+       rlwinm  r4,r7,27,31,31                  /* extract MSR[IS] */
+       mfspr   r7, SPRN_PID0
+       slwi    r7,r7,16
+       or      r7,r7,r4
+       mtspr   SPRN_MAS6,r7
+       tlbsx   0,r6                            /* search MSR[IS], SPID=PID0 */
+       mfspr   r7,SPRN_MAS1
+       andis.  r7,r7,MAS1_VALID@h
+       bne     match_TLB
+       mfspr   r7,SPRN_PID1
+       slwi    r7,r7,16
+       or      r7,r7,r4
+       mtspr   SPRN_MAS6,r7
+       tlbsx   0,r6                            /* search MSR[IS], SPID=PID1 */
+       mfspr   r7,SPRN_MAS1
+       andis.  r7,r7,MAS1_VALID@h
+       bne     match_TLB
+       mfspr   r7, SPRN_PID2
+       slwi    r7,r7,16
+       or      r7,r7,r4
+       mtspr   SPRN_MAS6,r7
+       tlbsx   0,r6                            /* Fall through, we had to match */
+match_TLB:
+       mfspr   r7,SPRN_MAS0
+       rlwinm  r3,r7,16,20,31                  /* Extract MAS0(Entry) */
+
+       mfspr   r7,SPRN_MAS1                    /* Insure IPROT set */
+       oris    r7,r7,MAS1_IPROT@h
+       mtspr   SPRN_MAS1,r7
+       tlbwe
+
+/* 2. Invalidate all entries except the entry we're executing in */
+       mfspr   r9,SPRN_TLB1CFG
+       andi.   r9,r9,0xfff
+       li      r6,0                            /* Set Entry counter to 0 */
+1:     lis     r7,0x1000                       /* Set MAS0(TLBSEL) = 1 */
+       rlwimi  r7,r6,16,4,15                   /* Setup MAS0 = TLBSEL | ESEL(r6) */
+       mtspr   SPRN_MAS0,r7
+       tlbre
+       mfspr   r7,SPRN_MAS1
+       rlwinm  r7,r7,0,2,31                    /* Clear MAS1 Valid and IPROT */
+       cmpw    r3,r6
+       beq     skpinv                          /* Dont update the current execution TLB */
+       mtspr   SPRN_MAS1,r7
+       tlbwe
+       isync
+skpinv:        addi    r6,r6,1                         /* Increment */
+       cmpw    r6,r9                           /* Are we done? */
+       bne     1b                              /* If not, repeat */
+
+       /* Invalidate TLB0 */
+       li      r6,0x04
+       tlbivax 0,r6
+#ifdef CONFIG_SMP
+       tlbsync
+#endif
+       /* Invalidate TLB1 */
+       li      r6,0x0c
+       tlbivax 0,r6
+#ifdef CONFIG_SMP
+       tlbsync
+#endif
+       msync
+
+/* 3. Setup a temp mapping and jump to it */
+       andi.   r5, r3, 0x1     /* Find an entry not used and is non-zero */
+       addi    r5, r5, 0x1
+       lis     r7,0x1000       /* Set MAS0(TLBSEL) = 1 */
+       rlwimi  r7,r3,16,4,15   /* Setup MAS0 = TLBSEL | ESEL(r3) */
+       mtspr   SPRN_MAS0,r7
+       tlbre
+
+       /* Just modify the entry ID and EPN for the temp mapping */
+       lis     r7,0x1000       /* Set MAS0(TLBSEL) = 1 */
+       rlwimi  r7,r5,16,4,15   /* Setup MAS0 = TLBSEL | ESEL(r5) */
+       mtspr   SPRN_MAS0,r7
+       xori    r6,r4,1         /* Setup TMP mapping in the other Address space */
+       slwi    r6,r6,12
+       oris    r6,r6,(MAS1_VALID|MAS1_IPROT)@h
+       ori     r6,r6,(MAS1_TSIZE(BOOKE_PAGESZ_4K))@l
+       mtspr   SPRN_MAS1,r6
+       mfspr   r6,SPRN_MAS2
+       li      r7,0            /* temp EPN = 0 */
+       rlwimi  r7,r6,0,20,31
+       mtspr   SPRN_MAS2,r7
+       tlbwe
+
+       xori    r6,r4,1
+       slwi    r6,r6,5         /* setup new context with other address space */
+       bl      1f              /* Find our address */
+1:     mflr    r9
+       rlwimi  r7,r9,0,20,31
+       addi    r7,r7,24
+       mtspr   SPRN_SRR0,r7
+       mtspr   SPRN_SRR1,r6
+       rfi
+
+/* 4. Clear out PIDs & Search info */
+       li      r6,0
+       mtspr   SPRN_PID0,r6
+       mtspr   SPRN_PID1,r6
+       mtspr   SPRN_PID2,r6
+       mtspr   SPRN_MAS6,r6
+
+/* 5. Invalidate mapping we started in */
+       lis     r7,0x1000       /* Set MAS0(TLBSEL) = 1 */
+       rlwimi  r7,r3,16,4,15   /* Setup MAS0 = TLBSEL | ESEL(r3) */
+       mtspr   SPRN_MAS0,r7
+       tlbre
+       li      r6,0
+       mtspr   SPRN_MAS1,r6
+       tlbwe
+       /* Invalidate TLB1 */
+       li      r9,0x0c
+       tlbivax 0,r9
+#ifdef CONFIG_SMP
+       tlbsync
+#endif
+       msync
+
+/* 6. Setup KERNELBASE mapping in TLB1[0] */
+       lis     r6,0x1000               /* Set MAS0(TLBSEL) = TLB1(1), ESEL = 0 */
+       mtspr   SPRN_MAS0,r6
+       lis     r6,(MAS1_VALID|MAS1_IPROT)@h
+       ori     r6,r6,(MAS1_TSIZE(BOOKE_PAGESZ_16M))@l
+       mtspr   SPRN_MAS1,r6
+       li      r7,0
+       lis     r6,KERNELBASE@h
+       ori     r6,r6,KERNELBASE@l
+       rlwimi  r6,r7,0,20,31
+       mtspr   SPRN_MAS2,r6
+       li      r7,(MAS3_SX|MAS3_SW|MAS3_SR)
+       mtspr   SPRN_MAS3,r7
+       tlbwe
+
+/* 7. Jump to KERNELBASE mapping */
+       lis     r7,MSR_KERNEL@h
+       ori     r7,r7,MSR_KERNEL@l
+       bl      1f                      /* Find our address */
+1:     mflr    r9
+       rlwimi  r6,r9,0,20,31
+       addi    r6,r6,24
+       mtspr   SPRN_SRR0,r6
+       mtspr   SPRN_SRR1,r7
+       rfi                             /* start execution out of TLB1[0] entry */
+
+/* 8. Clear out the temp mapping */
+       lis     r7,0x1000       /* Set MAS0(TLBSEL) = 1 */
+       rlwimi  r7,r5,16,4,15   /* Setup MAS0 = TLBSEL | ESEL(r5) */
+       mtspr   SPRN_MAS0,r7
+       tlbre
+       mtspr   SPRN_MAS1,r8
+       tlbwe
+       /* Invalidate TLB1 */
+       li      r9,0x0c
+       tlbivax 0,r9
+#ifdef CONFIG_SMP
+       tlbsync
+#endif
+       msync
+
+       /* Establish the interrupt vector offsets */
+       SET_IVOR(0,  CriticalInput);
+       SET_IVOR(1,  MachineCheck);
+       SET_IVOR(2,  DataStorage);
+       SET_IVOR(3,  InstructionStorage);
+       SET_IVOR(4,  ExternalInput);
+       SET_IVOR(5,  Alignment);
+       SET_IVOR(6,  Program);
+       SET_IVOR(7,  FloatingPointUnavailable);
+       SET_IVOR(8,  SystemCall);
+       SET_IVOR(9,  AuxillaryProcessorUnavailable);
+       SET_IVOR(10, Decrementer);
+       SET_IVOR(11, FixedIntervalTimer);
+       SET_IVOR(12, WatchdogTimer);
+       SET_IVOR(13, DataTLBError);
+       SET_IVOR(14, InstructionTLBError);
+       SET_IVOR(15, Debug);
+       SET_IVOR(32, SPEUnavailable);
+       SET_IVOR(33, SPEFloatingPointData);
+       SET_IVOR(34, SPEFloatingPointRound);
+       SET_IVOR(35, PerformanceMonitor);
+
+       /* Establish the interrupt vector base */
+       lis     r4,interrupt_base@h     /* IVPR only uses the high 16-bits */
+       mtspr   SPRN_IVPR,r4
+
+       /* Setup the defaults for TLB entries */
+       li      r2,(MAS4_TSIZED(BOOKE_PAGESZ_4K))@l
+       mtspr   SPRN_MAS4, r2
+
+#if 0
+       /* Enable DOZE */
+       mfspr   r2,SPRN_HID0
+       oris    r2,r2,HID0_DOZE@h
+       mtspr   SPRN_HID0, r2
+#endif
+
+#if !defined(CONFIG_BDI_SWITCH)
+       /*
+        * The Abatron BDI JTAG debugger does not tolerate others
+        * mucking with the debug registers.
+        */
+       lis     r2,DBCR0_IDM@h
+       mtspr   SPRN_DBCR0,r2
+       /* clear any residual debug events */
+       li      r2,-1
+       mtspr   SPRN_DBSR,r2
+#endif
+
+       /*
+        * This is where the main kernel code starts.
+        */
+
+       /* ptr to current */
+       lis     r2,init_task@h
+       ori     r2,r2,init_task@l
+
+       /* ptr to current thread */
+       addi    r4,r2,THREAD    /* init task's THREAD */
+       mtspr   SPRN_SPRG3,r4
+
+       /* stack */
+       lis     r1,init_thread_union@h
+       ori     r1,r1,init_thread_union@l
+       li      r0,0
+       stwu    r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1)
+
+       bl      early_init
+
+       mfspr   r3,SPRN_TLB1CFG
+       andi.   r3,r3,0xfff
+       lis     r4,num_tlbcam_entries@ha
+       stw     r3,num_tlbcam_entries@l(r4)
+/*
+ * Decide what sort of machine this is and initialize the MMU.
+ */
+       mr      r3,r31
+       mr      r4,r30
+       mr      r5,r29
+       mr      r6,r28
+       mr      r7,r27
+       bl      machine_init
+       bl      MMU_init
+
+       /* Setup PTE pointers for the Abatron bdiGDB */
+       lis     r6, swapper_pg_dir@h
+       ori     r6, r6, swapper_pg_dir@l
+       lis     r5, abatron_pteptrs@h
+       ori     r5, r5, abatron_pteptrs@l
+       lis     r4, KERNELBASE@h
+       ori     r4, r4, KERNELBASE@l
+       stw     r5, 0(r4)       /* Save abatron_pteptrs at a fixed location */
+       stw     r6, 0(r5)
+
+       /* Let's move on */
+       lis     r4,start_kernel@h
+       ori     r4,r4,start_kernel@l
+       lis     r3,MSR_KERNEL@h
+       ori     r3,r3,MSR_KERNEL@l
+       mtspr   SPRN_SRR0,r4
+       mtspr   SPRN_SRR1,r3
+       rfi                     /* change context and jump to start_kernel */
+
+/* Macros to hide the PTE size differences
+ *
+ * FIND_PTE -- walks the page tables given EA & pgdir pointer
+ *   r10 -- EA of fault
+ *   r11 -- PGDIR pointer
+ *   r12 -- free
+ *   label 2: is the bailout case
+ *
+ * if we find the pte (fall through):
+ *   r11 is low pte word
+ *   r12 is pointer to the pte
+ */
+#ifdef CONFIG_PTE_64BIT
+#define PTE_FLAGS_OFFSET       4
+#define FIND_PTE       \
+       rlwinm  r12, r10, 13, 19, 29;   /* Compute pgdir/pmd offset */  \
+       lwzx    r11, r12, r11;          /* Get pgd/pmd entry */         \
+       rlwinm. r12, r11, 0, 0, 20;     /* Extract pt base address */   \
+       beq     2f;                     /* Bail if no table */          \
+       rlwimi  r12, r10, 23, 20, 28;   /* Compute pte address */       \
+       lwz     r11, 4(r12);            /* Get pte entry */
+#else
+#define PTE_FLAGS_OFFSET       0
+#define FIND_PTE       \
+       rlwimi  r11, r10, 12, 20, 29;   /* Create L1 (pgdir/pmd) address */     \
+       lwz     r11, 0(r11);            /* Get L1 entry */                      \
+       rlwinm. r12, r11, 0, 0, 19;     /* Extract L2 (pte) base address */     \
+       beq     2f;                     /* Bail if no table */                  \
+       rlwimi  r12, r10, 22, 20, 29;   /* Compute PTE address */               \
+       lwz     r11, 0(r12);            /* Get Linux PTE */
+#endif
+
+/*
+ * Interrupt vector entry code
+ *
+ * The Book E MMUs are always on so we don't need to handle
+ * interrupts in real mode as with previous PPC processors. In
+ * this case we handle interrupts in the kernel virtual address
+ * space.
+ *
+ * Interrupt vectors are dynamically placed relative to the
+ * interrupt prefix as determined by the address of interrupt_base.
+ * The interrupt vectors offsets are programmed using the labels
+ * for each interrupt vector entry.
+ *
+ * Interrupt vectors must be aligned on a 16 byte boundary.
+ * We align on a 32 byte cache line boundary for good measure.
+ */
+
+interrupt_base:
+       /* Critical Input Interrupt */
+       CRITICAL_EXCEPTION(0x0100, CriticalInput, UnknownException)
+
+       /* Machine Check Interrupt */
+       MCHECK_EXCEPTION(0x0200, MachineCheck, MachineCheckException)
+
+       /* Data Storage Interrupt */
+       START_EXCEPTION(DataStorage)
+       mtspr   SPRN_SPRG0, r10         /* Save some working registers */
+       mtspr   SPRN_SPRG1, r11
+       mtspr   SPRN_SPRG4W, r12
+       mtspr   SPRN_SPRG5W, r13
+       mfcr    r11
+       mtspr   SPRN_SPRG7W, r11
+
+       /*
+        * Check if it was a store fault, if not then bail
+        * because a user tried to access a kernel or
+        * read-protected page.  Otherwise, get the
+        * offending address and handle it.
+        */
+       mfspr   r10, SPRN_ESR
+       andis.  r10, r10, ESR_ST@h
+       beq     2f
+
+       mfspr   r10, SPRN_DEAR          /* Get faulting address */
+
+       /* If we are faulting a kernel address, we have to use the
+        * kernel page tables.
+        */
+       lis     r11, TASK_SIZE@h
+       ori     r11, r11, TASK_SIZE@l
+       cmplw   0, r10, r11
+       bge     2f
+
+       /* Get the PGD for the current thread */
+3:
+       mfspr   r11,SPRN_SPRG3
+       lwz     r11,PGDIR(r11)
+4:
+       FIND_PTE
+
+       /* Are _PAGE_USER & _PAGE_RW set & _PAGE_HWWRITE not? */
+       andi.   r13, r11, _PAGE_RW|_PAGE_USER|_PAGE_HWWRITE
+       cmpwi   0, r13, _PAGE_RW|_PAGE_USER
+       bne     2f                      /* Bail if not */
+
+       /* Update 'changed'. */
+       ori     r11, r11, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE
+       stw     r11, PTE_FLAGS_OFFSET(r12) /* Update Linux page table */
+
+       /* MAS2 not updated as the entry does exist in the tlb, this
+          fault taken to detect state transition (eg: COW -> DIRTY)
+        */
+       andi.   r11, r11, _PAGE_HWEXEC
+       rlwimi  r11, r11, 31, 27, 27    /* SX <- _PAGE_HWEXEC */
+       ori     r11, r11, (MAS3_UW|MAS3_SW|MAS3_UR|MAS3_SR)@l /* set static perms */
+
+       /* update search PID in MAS6, AS = 0 */
+       mfspr   r12, SPRN_PID0
+       slwi    r12, r12, 16
+       mtspr   SPRN_MAS6, r12
+
+       /* find the TLB index that caused the fault.  It has to be here. */
+       tlbsx   0, r10
+
+       /* only update the perm bits, assume the RPN is fine */
+       mfspr   r12, SPRN_MAS3
+       rlwimi  r12, r11, 0, 20, 31
+       mtspr   SPRN_MAS3,r12
+       tlbwe
+
+       /* Done...restore registers and get out of here.  */
+       mfspr   r11, SPRN_SPRG7R
+       mtcr    r11
+       mfspr   r13, SPRN_SPRG5R
+       mfspr   r12, SPRN_SPRG4R
+       mfspr   r11, SPRN_SPRG1
+       mfspr   r10, SPRN_SPRG0
+       rfi                     /* Force context change */
+
+2:
+       /*
+        * The bailout.  Restore registers to pre-exception conditions
+        * and call the heavyweights to help us out.
+        */
+       mfspr   r11, SPRN_SPRG7R
+       mtcr    r11
+       mfspr   r13, SPRN_SPRG5R
+       mfspr   r12, SPRN_SPRG4R
+       mfspr   r11, SPRN_SPRG1
+       mfspr   r10, SPRN_SPRG0
+       b       data_access
+
+       /* Instruction Storage Interrupt */
+       INSTRUCTION_STORAGE_EXCEPTION
+
+       /* External Input Interrupt */
+       EXCEPTION(0x0500, ExternalInput, do_IRQ, EXC_XFER_LITE)
+
+       /* Alignment Interrupt */
+       ALIGNMENT_EXCEPTION
+
+       /* Program Interrupt */
+       PROGRAM_EXCEPTION
+
+       /* Floating Point Unavailable Interrupt */
+#ifdef CONFIG_PPC_FPU
+       FP_UNAVAILABLE_EXCEPTION
+#else
+       EXCEPTION(0x0800, FloatingPointUnavailable, UnknownException, EXC_XFER_EE)
+#endif
+
+       /* System Call Interrupt */
+       START_EXCEPTION(SystemCall)
+       NORMAL_EXCEPTION_PROLOG
+       EXC_XFER_EE_LITE(0x0c00, DoSyscall)
+
+       /* Auxillary Processor Unavailable Interrupt */
+       EXCEPTION(0x2900, AuxillaryProcessorUnavailable, UnknownException, EXC_XFER_EE)
+
+       /* Decrementer Interrupt */
+       DECREMENTER_EXCEPTION
+
+       /* Fixed Internal Timer Interrupt */
+       /* TODO: Add FIT support */
+       EXCEPTION(0x3100, FixedIntervalTimer, UnknownException, EXC_XFER_EE)
+
+       /* Watchdog Timer Interrupt */
+       /* TODO: Add watchdog support */
+       CRITICAL_EXCEPTION(0x3200, WatchdogTimer, UnknownException)
+
+       /* Data TLB Error Interrupt */
+       START_EXCEPTION(DataTLBError)
+       mtspr   SPRN_SPRG0, r10         /* Save some working registers */
+       mtspr   SPRN_SPRG1, r11
+       mtspr   SPRN_SPRG4W, r12
+       mtspr   SPRN_SPRG5W, r13
+       mfcr    r11
+       mtspr   SPRN_SPRG7W, r11
+       mfspr   r10, SPRN_DEAR          /* Get faulting address */
+
+       /* If we are faulting a kernel address, we have to use the
+        * kernel page tables.
+        */
+       lis     r11, TASK_SIZE@h
+       ori     r11, r11, TASK_SIZE@l
+       cmplw   5, r10, r11
+       blt     5, 3f
+       lis     r11, swapper_pg_dir@h
+       ori     r11, r11, swapper_pg_dir@l
+
+       mfspr   r12,SPRN_MAS1           /* Set TID to 0 */
+       rlwinm  r12,r12,0,16,1
+       mtspr   SPRN_MAS1,r12
+
+       b       4f
+
+       /* Get the PGD for the current thread */
+3:
+       mfspr   r11,SPRN_SPRG3
+       lwz     r11,PGDIR(r11)
+
+4:
+       FIND_PTE
+       andi.   r13, r11, _PAGE_PRESENT /* Is the page present? */
+       beq     2f                      /* Bail if not present */
+
+#ifdef CONFIG_PTE_64BIT
+       lwz     r13, 0(r12)
+#endif
+       ori     r11, r11, _PAGE_ACCESSED
+       stw     r11, PTE_FLAGS_OFFSET(r12)
+
+        /* Jump to common tlb load */
+       b       finish_tlb_load
+2:
+       /* The bailout.  Restore registers to pre-exception conditions
+        * and call the heavyweights to help us out.
+        */
+       mfspr   r11, SPRN_SPRG7R
+       mtcr    r11
+       mfspr   r13, SPRN_SPRG5R
+       mfspr   r12, SPRN_SPRG4R
+       mfspr   r11, SPRN_SPRG1
+       mfspr   r10, SPRN_SPRG0
+       b       data_access
+
+       /* Instruction TLB Error Interrupt */
+       /*
+        * Nearly the same as above, except we get our
+        * information from different registers and bailout
+        * to a different point.
+        */
+       START_EXCEPTION(InstructionTLBError)
+       mtspr   SPRN_SPRG0, r10         /* Save some working registers */
+       mtspr   SPRN_SPRG1, r11
+       mtspr   SPRN_SPRG4W, r12
+       mtspr   SPRN_SPRG5W, r13
+       mfcr    r11
+       mtspr   SPRN_SPRG7W, r11
+       mfspr   r10, SPRN_SRR0          /* Get faulting address */
+
+       /* If we are faulting a kernel address, we have to use the
+        * kernel page tables.
+        */
+       lis     r11, TASK_SIZE@h
+       ori     r11, r11, TASK_SIZE@l
+       cmplw   5, r10, r11
+       blt     5, 3f
+       lis     r11, swapper_pg_dir@h
+       ori     r11, r11, swapper_pg_dir@l
+
+       mfspr   r12,SPRN_MAS1           /* Set TID to 0 */
+       rlwinm  r12,r12,0,16,1
+       mtspr   SPRN_MAS1,r12
+
+       b       4f
+
+       /* Get the PGD for the current thread */
+3:
+       mfspr   r11,SPRN_SPRG3
+       lwz     r11,PGDIR(r11)
+
+4:
+       FIND_PTE
+       andi.   r13, r11, _PAGE_PRESENT /* Is the page present? */
+       beq     2f                      /* Bail if not present */
+
+#ifdef CONFIG_PTE_64BIT
+       lwz     r13, 0(r12)
+#endif
+       ori     r11, r11, _PAGE_ACCESSED
+       stw     r11, PTE_FLAGS_OFFSET(r12)
+
+       /* Jump to common TLB load point */
+       b       finish_tlb_load
+
+2:
+       /* The bailout.  Restore registers to pre-exception conditions
+        * and call the heavyweights to help us out.
+        */
+       mfspr   r11, SPRN_SPRG7R
+       mtcr    r11
+       mfspr   r13, SPRN_SPRG5R
+       mfspr   r12, SPRN_SPRG4R
+       mfspr   r11, SPRN_SPRG1
+       mfspr   r10, SPRN_SPRG0
+       b       InstructionStorage
+
+#ifdef CONFIG_SPE
+       /* SPE Unavailable */
+       START_EXCEPTION(SPEUnavailable)
+       NORMAL_EXCEPTION_PROLOG
+       bne     load_up_spe
+       addi    r3,r1,STACK_FRAME_OVERHEAD
+       EXC_XFER_EE_LITE(0x2010, KernelSPE)
+#else
+       EXCEPTION(0x2020, SPEUnavailable, UnknownException, EXC_XFER_EE)
+#endif /* CONFIG_SPE */
+
+       /* SPE Floating Point Data */
+#ifdef CONFIG_SPE
+       EXCEPTION(0x2030, SPEFloatingPointData, SPEFloatingPointException, EXC_XFER_EE);
+#else
+       EXCEPTION(0x2040, SPEFloatingPointData, UnknownException, EXC_XFER_EE)
+#endif /* CONFIG_SPE */
+
+       /* SPE Floating Point Round */
+       EXCEPTION(0x2050, SPEFloatingPointRound, UnknownException, EXC_XFER_EE)
+
+       /* Performance Monitor */
+       EXCEPTION(0x2060, PerformanceMonitor, PerformanceMonitorException, EXC_XFER_STD)
+
+
+       /* Debug Interrupt */
+       DEBUG_EXCEPTION
+
+/*
+ * Local functions
+ */
+       /*
+        * Data TLB exceptions will bail out to this point
+        * if they can't resolve the lightweight TLB fault.
+        */
+data_access:
+       NORMAL_EXCEPTION_PROLOG
+       mfspr   r5,SPRN_ESR             /* Grab the ESR, save it, pass arg3 */
+       stw     r5,_ESR(r11)
+       mfspr   r4,SPRN_DEAR            /* Grab the DEAR, save it, pass arg2 */
+       andis.  r10,r5,(ESR_ILK|ESR_DLK)@h
+       bne     1f
+       EXC_XFER_EE_LITE(0x0300, handle_page_fault)
+1:
+       addi    r3,r1,STACK_FRAME_OVERHEAD
+       EXC_XFER_EE_LITE(0x0300, CacheLockingException)
+
+/*
+
+ * Both the instruction and data TLB miss get to this
+ * point to load the TLB.
+ *     r10 - EA of fault
+ *     r11 - TLB (info from Linux PTE)
+ *     r12, r13 - available to use
+ *     CR5 - results of addr < TASK_SIZE
+ *     MAS0, MAS1 - loaded with proper value when we get here
+ *     MAS2, MAS3 - will need additional info from Linux PTE
+ *     Upon exit, we reload everything and RFI.
+ */
+finish_tlb_load:
+       /*
+        * We set execute, because we don't have the granularity to
+        * properly set this at the page level (Linux problem).
+        * Many of these bits are software only.  Bits we don't set
+        * here we (properly should) assume have the appropriate value.
+        */
+
+       mfspr   r12, SPRN_MAS2
+#ifdef CONFIG_PTE_64BIT
+       rlwimi  r12, r11, 26, 24, 31    /* extract ...WIMGE from pte */
+#else
+       rlwimi  r12, r11, 26, 27, 31    /* extract WIMGE from pte */
+#endif
+       mtspr   SPRN_MAS2, r12
+
+       bge     5, 1f
+
+       /* is user addr */
+       andi.   r12, r11, (_PAGE_USER | _PAGE_HWWRITE | _PAGE_HWEXEC)
+       andi.   r10, r11, _PAGE_USER    /* Test for _PAGE_USER */
+       srwi    r10, r12, 1
+       or      r12, r12, r10   /* Copy user perms into supervisor */
+       iseleq  r12, 0, r12
+       b       2f
+
+       /* is kernel addr */
+1:     rlwinm  r12, r11, 31, 29, 29    /* Extract _PAGE_HWWRITE into SW */
+       ori     r12, r12, (MAS3_SX | MAS3_SR)
+
+#ifdef CONFIG_PTE_64BIT
+2:     rlwimi  r12, r13, 24, 0, 7      /* grab RPN[32:39] */
+       rlwimi  r12, r11, 24, 8, 19     /* grab RPN[40:51] */
+       mtspr   SPRN_MAS3, r12
+BEGIN_FTR_SECTION
+       srwi    r10, r13, 8             /* grab RPN[8:31] */
+       mtspr   SPRN_MAS7, r10
+END_FTR_SECTION_IFSET(CPU_FTR_BIG_PHYS)
+#else
+2:     rlwimi  r11, r12, 0, 20, 31     /* Extract RPN from PTE and merge with perms */
+       mtspr   SPRN_MAS3, r11
+#endif
+       tlbwe
+
+       /* Done...restore registers and get out of here.  */
+       mfspr   r11, SPRN_SPRG7R
+       mtcr    r11
+       mfspr   r13, SPRN_SPRG5R
+       mfspr   r12, SPRN_SPRG4R
+       mfspr   r11, SPRN_SPRG1
+       mfspr   r10, SPRN_SPRG0
+       rfi                                     /* Force context change */
+
+#ifdef CONFIG_SPE
+/* Note that the SPE support is closely modeled after the AltiVec
+ * support.  Changes to one are likely to be applicable to the
+ * other!  */
+load_up_spe:
+/*
+ * Disable SPE for the task which had SPE previously,
+ * and save its SPE registers in its thread_struct.
+ * Enables SPE for use in the kernel on return.
+ * On SMP we know the SPE units are free, since we give it up every
+ * switch.  -- Kumar
+ */
+       mfmsr   r5
+       oris    r5,r5,MSR_SPE@h
+       mtmsr   r5                      /* enable use of SPE now */
+       isync
+/*
+ * For SMP, we don't do lazy SPE switching because it just gets too
+ * horrendously complex, especially when a task switches from one CPU
+ * to another.  Instead we call giveup_spe in switch_to.
+ */
+#ifndef CONFIG_SMP
+       lis     r3,last_task_used_spe@ha
+       lwz     r4,last_task_used_spe@l(r3)
+       cmpi    0,r4,0
+       beq     1f
+       addi    r4,r4,THREAD    /* want THREAD of last_task_used_spe */
+       SAVE_32EVR(0,r10,r4)
+       evxor   evr10, evr10, evr10     /* clear out evr10 */
+       evmwumiaa evr10, evr10, evr10   /* evr10 <- ACC = 0 * 0 + ACC */
+       li      r5,THREAD_ACC
+       evstddx evr10, r4, r5           /* save off accumulator */
+       lwz     r5,PT_REGS(r4)
+       lwz     r4,_MSR-STACK_FRAME_OVERHEAD(r5)
+       lis     r10,MSR_SPE@h
+       andc    r4,r4,r10       /* disable SPE for previous task */
+       stw     r4,_MSR-STACK_FRAME_OVERHEAD(r5)
+1:
+#endif /* CONFIG_SMP */
+       /* enable use of SPE after return */
+       oris    r9,r9,MSR_SPE@h
+       mfspr   r5,SPRN_SPRG3           /* current task's THREAD (phys) */
+       li      r4,1
+       li      r10,THREAD_ACC
+       stw     r4,THREAD_USED_SPE(r5)
+       evlddx  evr4,r10,r5
+       evmra   evr4,evr4
+       REST_32EVR(0,r10,r5)
+#ifndef CONFIG_SMP
+       subi    r4,r5,THREAD
+       stw     r4,last_task_used_spe@l(r3)
+#endif /* CONFIG_SMP */
+       /* restore registers and return */
+2:     REST_4GPRS(3, r11)
+       lwz     r10,_CCR(r11)
+       REST_GPR(1, r11)
+       mtcr    r10
+       lwz     r10,_LINK(r11)
+       mtlr    r10
+       REST_GPR(10, r11)
+       mtspr   SPRN_SRR1,r9
+       mtspr   SPRN_SRR0,r12
+       REST_GPR(9, r11)
+       REST_GPR(12, r11)
+       lwz     r11,GPR11(r11)
+       SYNC
+       rfi
+
+/*
+ * SPE unavailable trap from kernel - print a message, but let
+ * the task use SPE in the kernel until it returns to user mode.
+ */
+KernelSPE:
+       lwz     r3,_MSR(r1)
+       oris    r3,r3,MSR_SPE@h
+       stw     r3,_MSR(r1)     /* enable use of SPE after return */
+       lis     r3,87f@h
+       ori     r3,r3,87f@l
+       mr      r4,r2           /* current */
+       lwz     r5,_NIP(r1)
+       bl      printk
+       b       ret_from_except
+87:    .string "SPE used in kernel  (task=%p, pc=%x)  \n"
+       .align  4,0
+
+#endif /* CONFIG_SPE */
+
+/*
+ * Global functions
+ */
+
+/*
+ * extern void loadcam_entry(unsigned int index)
+ *
+ * Load TLBCAM[index] entry in to the L2 CAM MMU
+ */
+_GLOBAL(loadcam_entry)
+       lis     r4,TLBCAM@ha
+       addi    r4,r4,TLBCAM@l
+       mulli   r5,r3,20
+       add     r3,r5,r4
+       lwz     r4,0(r3)
+       mtspr   SPRN_MAS0,r4
+       lwz     r4,4(r3)
+       mtspr   SPRN_MAS1,r4
+       lwz     r4,8(r3)
+       mtspr   SPRN_MAS2,r4
+       lwz     r4,12(r3)
+       mtspr   SPRN_MAS3,r4
+       tlbwe
+       isync
+       blr
+
+/*
+ * extern void giveup_altivec(struct task_struct *prev)
+ *
+ * The e500 core does not have an AltiVec unit.
+ */
+_GLOBAL(giveup_altivec)
+       blr
+
+#ifdef CONFIG_SPE
+/*
+ * extern void giveup_spe(struct task_struct *prev)
+ *
+ */
+_GLOBAL(giveup_spe)
+       mfmsr   r5
+       oris    r5,r5,MSR_SPE@h
+       SYNC
+       mtmsr   r5                      /* enable use of SPE now */
+       isync
+       cmpi    0,r3,0
+       beqlr-                          /* if no previous owner, done */
+       addi    r3,r3,THREAD            /* want THREAD of task */
+       lwz     r5,PT_REGS(r3)
+       cmpi    0,r5,0
+       SAVE_32EVR(0, r4, r3)
+       evxor   evr6, evr6, evr6        /* clear out evr6 */
+       evmwumiaa evr6, evr6, evr6      /* evr6 <- ACC = 0 * 0 + ACC */
+       li      r4,THREAD_ACC
+       evstddx evr6, r4, r3            /* save off accumulator */
+       mfspr   r6,SPRN_SPEFSCR
+       stw     r6,THREAD_SPEFSCR(r3)   /* save spefscr register value */
+       beq     1f
+       lwz     r4,_MSR-STACK_FRAME_OVERHEAD(r5)
+       lis     r3,MSR_SPE@h
+       andc    r4,r4,r3                /* disable SPE for previous task */
+       stw     r4,_MSR-STACK_FRAME_OVERHEAD(r5)
+1:
+#ifndef CONFIG_SMP
+       li      r5,0
+       lis     r4,last_task_used_spe@ha
+       stw     r5,last_task_used_spe@l(r4)
+#endif /* CONFIG_SMP */
+       blr
+#endif /* CONFIG_SPE */
+
+/*
+ * extern void giveup_fpu(struct task_struct *prev)
+ *
+ * Not all FSL Book-E cores have an FPU
+ */
+#ifndef CONFIG_PPC_FPU
+_GLOBAL(giveup_fpu)
+       blr
+#endif
+
+/*
+ * extern void abort(void)
+ *
+ * At present, this routine just applies a system reset.
+ */
+_GLOBAL(abort)
+       li      r13,0
+        mtspr   SPRN_DBCR0,r13         /* disable all debug events */
+       mfmsr   r13
+       ori     r13,r13,MSR_DE@l        /* Enable Debug Events */
+       mtmsr   r13
+        mfspr   r13,SPRN_DBCR0
+        lis    r13,(DBCR0_IDM|DBCR0_RST_CHIP)@h
+        mtspr   SPRN_DBCR0,r13
+
+_GLOBAL(set_context)
+
+#ifdef CONFIG_BDI_SWITCH
+       /* Context switch the PTE pointer for the Abatron BDI2000.
+        * The PGDIR is the second parameter.
+        */
+       lis     r5, abatron_pteptrs@h
+       ori     r5, r5, abatron_pteptrs@l
+       stw     r4, 0x4(r5)
+#endif
+       mtspr   SPRN_PID,r3
+       isync                   /* Force context change */
+       blr
+
+/*
+ * We put a few things here that have to be page-aligned. This stuff
+ * goes at the beginning of the data segment, which is page-aligned.
+ */
+       .data
+_GLOBAL(sdata)
+_GLOBAL(empty_zero_page)
+       .space  4096
+_GLOBAL(swapper_pg_dir)
+       .space  4096
+
+/* Reserved 4k for the critical exception stack & 4k for the machine
+ * check stack per CPU for kernel mode exceptions */
+       .section .bss
+        .align 12
+exception_stack_bottom:
+       .space  BOOKE_EXCEPTION_STACK_SIZE * NR_CPUS
+_GLOBAL(exception_stack_top)
+
+/*
+ * This space gets a copy of optional info passed to us by the bootstrap
+ * which is used to pass parameters into the kernel like root=/dev/sda1, etc.
+ */
+_GLOBAL(cmd_line)
+       .space  512
+
+/*
+ * Room for two PTE pointers, usually the kernel and current user pointers
+ * to their respective root page table.
+ */
+abatron_pteptrs:
+       .space  8
+
diff --git a/arch/ppc/kernel/swsusp.S b/arch/ppc/kernel/swsusp.S
new file mode 100644 (file)
index 0000000..55148bb
--- /dev/null
@@ -0,0 +1,349 @@
+#include <linux/config.h>
+#include <linux/threads.h>
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <asm/cputable.h>
+#include <asm/thread_info.h>
+#include <asm/ppc_asm.h>
+#include <asm/offsets.h>
+
+
+/*
+ * Structure for storing CPU registers on the save area.
+ */
+#define SL_SP          0
+#define SL_PC          4
+#define SL_MSR         8
+#define SL_SDR1                0xc
+#define SL_SPRG0       0x10    /* 4 sprg's */
+#define SL_DBAT0       0x20
+#define SL_IBAT0       0x28
+#define SL_DBAT1       0x30
+#define SL_IBAT1       0x38
+#define SL_DBAT2       0x40
+#define SL_IBAT2       0x48
+#define SL_DBAT3       0x50
+#define SL_IBAT3       0x58
+#define SL_TB          0x60
+#define SL_R2          0x68
+#define SL_CR          0x6c
+#define SL_LR          0x70
+#define SL_R12         0x74    /* r12 to r31 */
+#define SL_SIZE                (SL_R12 + 80)
+
+       .section .data
+       .align  5
+
+_GLOBAL(swsusp_save_area)
+       .space  SL_SIZE
+
+
+       .section .text
+       .align  5
+
+_GLOBAL(swsusp_arch_suspend)
+
+       lis     r11,swsusp_save_area@h
+       ori     r11,r11,swsusp_save_area@l
+
+       mflr    r0
+       stw     r0,SL_LR(r11)
+       mfcr    r0
+       stw     r0,SL_CR(r11)
+       stw     r1,SL_SP(r11)
+       stw     r2,SL_R2(r11)
+       stmw    r12,SL_R12(r11)
+
+       /* Save MSR & SDR1 */
+       mfmsr   r4
+       stw     r4,SL_MSR(r11)
+       mfsdr1  r4
+       stw     r4,SL_SDR1(r11)
+
+       /* Get a stable timebase and save it */
+1:     mftbu   r4
+       stw     r4,SL_TB(r11)
+       mftb    r5
+       stw     r5,SL_TB+4(r11)
+       mftbu   r3
+       cmpw    r3,r4
+       bne     1b
+
+       /* Save SPRGs */
+       mfsprg  r4,0
+       stw     r4,SL_SPRG0(r11)
+       mfsprg  r4,1
+       stw     r4,SL_SPRG0+4(r11)
+       mfsprg  r4,2
+       stw     r4,SL_SPRG0+8(r11)
+       mfsprg  r4,3
+       stw     r4,SL_SPRG0+12(r11)
+
+       /* Save BATs */
+       mfdbatu r4,0
+       stw     r4,SL_DBAT0(r11)
+       mfdbatl r4,0
+       stw     r4,SL_DBAT0+4(r11)
+       mfdbatu r4,1
+       stw     r4,SL_DBAT1(r11)
+       mfdbatl r4,1
+       stw     r4,SL_DBAT1+4(r11)
+       mfdbatu r4,2
+       stw     r4,SL_DBAT2(r11)
+       mfdbatl r4,2
+       stw     r4,SL_DBAT2+4(r11)
+       mfdbatu r4,3
+       stw     r4,SL_DBAT3(r11)
+       mfdbatl r4,3
+       stw     r4,SL_DBAT3+4(r11)
+       mfibatu r4,0
+       stw     r4,SL_IBAT0(r11)
+       mfibatl r4,0
+       stw     r4,SL_IBAT0+4(r11)
+       mfibatu r4,1
+       stw     r4,SL_IBAT1(r11)
+       mfibatl r4,1
+       stw     r4,SL_IBAT1+4(r11)
+       mfibatu r4,2
+       stw     r4,SL_IBAT2(r11)
+       mfibatl r4,2
+       stw     r4,SL_IBAT2+4(r11)
+       mfibatu r4,3
+       stw     r4,SL_IBAT3(r11)
+       mfibatl r4,3
+       stw     r4,SL_IBAT3+4(r11)
+
+#if  0
+       /* Backup various CPU config stuffs */
+       bl      __save_cpu_setup
+#endif
+       /* Call the low level suspend stuff (we should probably have made
+        * a stackframe...
+        */
+       bl      swsusp_save
+
+       /* Restore LR from the save area */
+       lis     r11,swsusp_save_area@h
+       ori     r11,r11,swsusp_save_area@l
+       lwz     r0,SL_LR(r11)
+       mtlr    r0
+
+       blr
+
+
+/* Resume code */
+_GLOBAL(swsusp_arch_resume)
+
+       /* Stop pending alitvec streams and memory accesses */
+BEGIN_FTR_SECTION
+       DSSALL
+END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
+       sync
+
+       /* Disable MSR:DR to make sure we don't take a TLB or
+        * hash miss during the copy, as our hash table will
+        * for a while be unuseable. For .text, we assume we are
+        * covered by a BAT. This works only for non-G5 at this
+        * point. G5 will need a better approach, possibly using
+        * a small temporary hash table filled with large mappings,
+        * disabling the MMU completely isn't a good option for
+        * performance reasons.
+        * (Note that 750's may have the same performance issue as
+        * the G5 in this case, we should investigate using moving
+        * BATs for these CPUs)
+        */
+       mfmsr   r0
+       sync
+       rlwinm  r0,r0,0,28,26           /* clear MSR_DR */
+       mtmsr   r0
+       sync
+       isync
+
+       /* Load ptr the list of pages to copy in r3 */
+       lis     r11,(pagedir_nosave - KERNELBASE)@h
+       ori     r11,r11,pagedir_nosave@l
+       lwz     r10,0(r11)
+
+       /* Copy the pages. This is a very basic implementation, to
+        * be replaced by something more cache efficient */
+1:
+       tophys(r3,r10)
+       li      r0,256
+       mtctr   r0
+       lwz     r11,pbe_address(r3)     /* source */
+       tophys(r5,r11)
+       lwz     r10,pbe_orig_address(r3)        /* destination */
+       tophys(r6,r10)
+2:
+       lwz     r8,0(r5)
+       lwz     r9,4(r5)
+       lwz     r10,8(r5)
+       lwz     r11,12(r5)
+       addi    r5,r5,16
+       stw     r8,0(r6)
+       stw     r9,4(r6)
+       stw     r10,8(r6)
+       stw     r11,12(r6)
+       addi    r6,r6,16
+       bdnz    2b
+       lwz             r10,pbe_next(r3)
+       cmpwi   0,r10,0
+       bne     1b
+
+       /* Do a very simple cache flush/inval of the L1 to ensure
+        * coherency of the icache
+        */
+       lis     r3,0x0002
+       mtctr   r3
+       li      r3, 0
+1:
+       lwz     r0,0(r3)
+       addi    r3,r3,0x0020
+       bdnz    1b
+       isync
+       sync
+
+       /* Now flush those cache lines */
+       lis     r3,0x0002
+       mtctr   r3
+       li      r3, 0
+1:
+       dcbf    0,r3
+       addi    r3,r3,0x0020
+       bdnz    1b
+       sync
+
+       /* Ok, we are now running with the kernel data of the old
+        * kernel fully restored. We can get to the save area
+        * easily now. As for the rest of the code, it assumes the
+        * loader kernel and the booted one are exactly identical
+        */
+       lis     r11,swsusp_save_area@h
+       ori     r11,r11,swsusp_save_area@l
+       tophys(r11,r11)
+
+#if 0
+       /* Restore various CPU config stuffs */
+       bl      __restore_cpu_setup
+#endif
+       /* Restore the BATs, and SDR1.  Then we can turn on the MMU.
+        * This is a bit hairy as we are running out of those BATs,
+        * but first, our code is probably in the icache, and we are
+        * writing the same value to the BAT, so that should be fine,
+        * though a better solution will have to be found long-term
+        */
+       lwz     r4,SL_SDR1(r11)
+       mtsdr1  r4
+       lwz     r4,SL_SPRG0(r11)
+       mtsprg  0,r4
+       lwz     r4,SL_SPRG0+4(r11)
+       mtsprg  1,r4
+       lwz     r4,SL_SPRG0+8(r11)
+       mtsprg  2,r4
+       lwz     r4,SL_SPRG0+12(r11)
+       mtsprg  3,r4
+
+#if 0
+       lwz     r4,SL_DBAT0(r11)
+       mtdbatu 0,r4
+       lwz     r4,SL_DBAT0+4(r11)
+       mtdbatl 0,r4
+       lwz     r4,SL_DBAT1(r11)
+       mtdbatu 1,r4
+       lwz     r4,SL_DBAT1+4(r11)
+       mtdbatl 1,r4
+       lwz     r4,SL_DBAT2(r11)
+       mtdbatu 2,r4
+       lwz     r4,SL_DBAT2+4(r11)
+       mtdbatl 2,r4
+       lwz     r4,SL_DBAT3(r11)
+       mtdbatu 3,r4
+       lwz     r4,SL_DBAT3+4(r11)
+       mtdbatl 3,r4
+       lwz     r4,SL_IBAT0(r11)
+       mtibatu 0,r4
+       lwz     r4,SL_IBAT0+4(r11)
+       mtibatl 0,r4
+       lwz     r4,SL_IBAT1(r11)
+       mtibatu 1,r4
+       lwz     r4,SL_IBAT1+4(r11)
+       mtibatl 1,r4
+       lwz     r4,SL_IBAT2(r11)
+       mtibatu 2,r4
+       lwz     r4,SL_IBAT2+4(r11)
+       mtibatl 2,r4
+       lwz     r4,SL_IBAT3(r11)
+       mtibatu 3,r4
+       lwz     r4,SL_IBAT3+4(r11)
+       mtibatl 3,r4
+#endif
+
+BEGIN_FTR_SECTION
+       li      r4,0
+       mtspr   SPRN_DBAT4U,r4
+       mtspr   SPRN_DBAT4L,r4
+       mtspr   SPRN_DBAT5U,r4
+       mtspr   SPRN_DBAT5L,r4
+       mtspr   SPRN_DBAT6U,r4
+       mtspr   SPRN_DBAT6L,r4
+       mtspr   SPRN_DBAT7U,r4
+       mtspr   SPRN_DBAT7L,r4
+       mtspr   SPRN_IBAT4U,r4
+       mtspr   SPRN_IBAT4L,r4
+       mtspr   SPRN_IBAT5U,r4
+       mtspr   SPRN_IBAT5L,r4
+       mtspr   SPRN_IBAT6U,r4
+       mtspr   SPRN_IBAT6L,r4
+       mtspr   SPRN_IBAT7U,r4
+       mtspr   SPRN_IBAT7L,r4
+END_FTR_SECTION_IFSET(CPU_FTR_HAS_HIGH_BATS)
+
+       /* Flush all TLBs */
+       lis     r4,0x1000
+1:     addic.  r4,r4,-0x1000
+       tlbie   r4
+       blt     1b
+       sync
+
+       /* restore the MSR and turn on the MMU */
+       lwz     r3,SL_MSR(r11)
+       bl      turn_on_mmu
+       tovirt(r11,r11)
+
+       /* Restore TB */
+       li      r3,0
+       mttbl   r3
+       lwz     r3,SL_TB(r11)
+       lwz     r4,SL_TB+4(r11)
+       mttbu   r3
+       mttbl   r4
+
+       /* Kick decrementer */
+       li      r0,1
+       mtdec   r0
+
+       /* Restore the callee-saved registers and return */
+       lwz     r0,SL_CR(r11)
+       mtcr    r0
+       lwz     r2,SL_R2(r11)
+       lmw     r12,SL_R12(r11)
+       lwz     r1,SL_SP(r11)
+       lwz     r0,SL_LR(r11)
+       mtlr    r0
+
+       // XXX Note: we don't really need to call swsusp_resume
+
+       li      r3,0
+       blr
+
+/* FIXME:This construct is actually not useful since we don't shut
+ * down the instruction MMU, we could just flip back MSR-DR on.
+ */
+turn_on_mmu:
+       mflr    r4
+       mtsrr0  r4
+       mtsrr1  r3
+       sync
+       isync
+       rfi
+
diff --git a/arch/ppc/platforms/83xx/mpc834x_sys.c b/arch/ppc/platforms/83xx/mpc834x_sys.c
new file mode 100644 (file)
index 0000000..e6348b5
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * arch/ppc/platforms/83xx/mpc834x_sys.c
+ *
+ * MPC834x SYS board specific routines
+ *
+ * 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/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/console.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/seq_file.h>
+#include <linux/root_dev.h>
+#include <linux/serial.h>
+#include <linux/tty.h> /* for linux/serial_core.h */
+#include <linux/serial_core.h>
+#include <linux/initrd.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/ipic.h>
+#include <asm/bootinfo.h>
+#include <asm/pci-bridge.h>
+#include <asm/mpc83xx.h>
+#include <asm/irq.h>
+#include <asm/kgdb.h>
+#include <asm/ppc_sys.h>
+#include <mm/mmu_decl.h>
+
+#include <syslib/ppc83xx_setup.h>
+
+#ifndef CONFIG_PCI
+unsigned long isa_io_base = 0;
+unsigned long isa_mem_base = 0;
+#endif
+
+extern unsigned long total_memory;     /* in mm/init */
+
+unsigned char __res[sizeof (bd_t)];
+
+#ifdef CONFIG_PCI
+#error "PCI is not supported"
+/* NEED mpc83xx_map_irq & mpc83xx_exclude_device
+   see platforms/85xx/mpc85xx_ads_common.c */
+#endif /* CONFIG_PCI */
+
+/* ************************************************************************
+ *
+ * Setup the architecture
+ *
+ */
+static void __init
+mpc834x_sys_setup_arch(void)
+{
+       bd_t *binfo = (bd_t *) __res;
+       unsigned int freq;
+       struct gianfar_platform_data *pdata;
+
+       /* get the core frequency */
+       freq = binfo->bi_intfreq;
+
+       /* 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 */
+       mpc83xx_sys_setup_hose();
+#endif
+       mpc83xx_early_serial_map();
+
+       /* setup the board related information for the enet controllers */
+       pdata = (struct gianfar_platform_data *) ppc_sys_get_pdata(MPC83xx_TSEC1);
+       pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR;
+       pdata->interruptPHY = MPC83xx_IRQ_EXT1;
+       pdata->phyid = 0;
+       /* fixup phy address */
+       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(MPC83xx_TSEC2);
+       pdata->board_flags = FSL_GIANFAR_BRD_HAS_PHY_INTR;
+       pdata->interruptPHY = MPC83xx_IRQ_EXT2;
+       pdata->phyid = 1;
+       /* 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
+}
+
+static void __init
+mpc834x_sys_map_io(void)
+{
+       /* we steal the lowest ioremap addr for virt space */
+       io_block_mapping(VIRT_IMMRBAR, immrbar, 1024*1024, _PAGE_IO);
+}
+
+int
+mpc834x_sys_show_cpuinfo(struct seq_file *m)
+{
+       uint pvid, svid, phid1;
+       bd_t *binfo = (bd_t *) __res;
+       unsigned int freq;
+
+       /* get the core frequency */
+       freq = binfo->bi_intfreq;
+
+       pvid = mfspr(SPRN_PVR);
+       svid = mfspr(SPRN_SVR);
+
+       seq_printf(m, "Vendor\t\t: Freescale Inc.\n");
+       seq_printf(m, "Machine\t\t: mpc%s sys\n", cur_ppc_sys_spec->ppc_sys_name);
+       seq_printf(m, "core clock\t: %d MHz\n"
+                       "bus  clock\t: %d MHz\n",
+                       (int)(binfo->bi_intfreq / 1000000),
+                       (int)(binfo->bi_busfreq / 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(SPRN_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", (int)(binfo->bi_memsize / (1024 * 1024)));
+
+       return 0;
+}
+
+
+void __init
+mpc834x_sys_init_IRQ(void)
+{
+       bd_t *binfo = (bd_t *) __res;
+
+       u8 senses[8] = {
+               0,                      /* EXT 0 */
+               IRQ_SENSE_LEVEL,        /* EXT 1 */
+               IRQ_SENSE_LEVEL,        /* EXT 2 */
+               0,                      /* EXT 3 */
+               0,                      /* EXT 4 */
+               0,                      /* EXT 5 */
+               0,                      /* EXT 6 */
+               0,                      /* EXT 7 */
+       };
+
+       ipic_init(binfo->bi_immr_base + 0x00700, 0, MPC83xx_IPIC_IRQ_OFFSET, senses, 8);
+
+       /* Initialize the default interrupt mapping priorities,
+        * in case the boot rom changed something on us.
+        */
+       ipic_set_default_priority();
+}
+
+static __inline__ void
+mpc834x_sys_set_bat(void)
+{
+       /* we steal the lowest ioremap addr for virt space */
+       mb();
+       mtspr(SPRN_DBAT1U, VIRT_IMMRBAR | 0x1e);
+       mtspr(SPRN_DBAT1L, immrbar | 0x2a);
+       mb();
+}
+
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+             unsigned long r6, unsigned long r7)
+{
+       bd_t *binfo = (bd_t *) __res;
+
+       /* 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));
+       }
+
+       immrbar = binfo->bi_immr_base;
+
+       mpc834x_sys_set_bat();
+
+#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG)
+       {
+               struct uart_port p;
+
+               memset(&p, 0, sizeof (p));
+               p.iotype = SERIAL_IO_MEM;
+               p.membase = (unsigned char __iomem *)(VIRT_IMMRBAR + 0x4500);
+               p.uartclk = binfo->bi_busfreq;
+
+               gen550_init(0, &p);
+
+               memset(&p, 0, sizeof (p));
+               p.iotype = SERIAL_IO_MEM;
+               p.membase = (unsigned char __iomem *)(VIRT_IMMRBAR + 0x4600);
+               p.uartclk = binfo->bi_busfreq;
+
+               gen550_init(1, &p);
+       }
+#endif
+
+       identify_ppc_sys_by_id(mfspr(SPRN_SVR));
+
+       /* setup the PowerPC module struct */
+       ppc_md.setup_arch = mpc834x_sys_setup_arch;
+       ppc_md.show_cpuinfo = mpc834x_sys_show_cpuinfo;
+
+       ppc_md.init_IRQ = mpc834x_sys_init_IRQ;
+       ppc_md.get_irq = ipic_get_irq;
+
+       ppc_md.restart = mpc83xx_restart;
+       ppc_md.power_off = mpc83xx_power_off;
+       ppc_md.halt = mpc83xx_halt;
+
+       ppc_md.find_end_of_memory = mpc83xx_find_end_of_memory;
+       ppc_md.setup_io_mappings  = mpc834x_sys_map_io;
+
+       ppc_md.time_init = mpc83xx_time_init;
+       ppc_md.set_rtc_time = NULL;
+       ppc_md.get_rtc_time = NULL;
+       ppc_md.calibrate_decr = mpc83xx_calibrate_decr;
+
+       ppc_md.early_serial_map = mpc83xx_early_serial_map;
+#if defined(CONFIG_SERIAL_8250) && defined(CONFIG_SERIAL_TEXT_DEBUG)
+       ppc_md.progress = gen550_progress;
+#endif /* CONFIG_SERIAL_8250 && CONFIG_SERIAL_TEXT_DEBUG */
+
+       if (ppc_md.progress)
+               ppc_md.progress("mpc834x_sys_init(): exit", 0);
+
+       return;
+}
diff --git a/arch/ppc/platforms/83xx/mpc834x_sys.h b/arch/ppc/platforms/83xx/mpc834x_sys.h
new file mode 100644 (file)
index 0000000..a2f6e49
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * arch/ppc/platforms/83xx/mpc834x_sys.h
+ *
+ * MPC834X SYS common board definitions
+ *
+ * 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.
+ *
+ */
+
+#ifndef __MACH_MPC83XX_SYS_H__
+#define __MACH_MPC83XX_SYS_H__
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <syslib/ppc83xx_setup.h>
+#include <asm/ppcboot.h>
+
+#define VIRT_IMMRBAR           ((uint)0xfe000000)
+
+#define BCSR_PHYS_ADDR         ((uint)0xf8000000)
+#define BCSR_SIZE              ((uint)(32 * 1024))
+
+#define BCSR_MISC_REG2_OFF     0x07
+#define BCSR_MISC_REG2_PORESET 0x01
+
+#define BCSR_MISC_REG3_OFF     0x08
+#define BCSR_MISC_REG3_CNFLOCK 0x80
+
+#ifdef CONFIG_PCI
+/* PCI interrupt controller */
+#define PIRQA        MPC83xx_IRQ_IRQ4
+#define PIRQB        MPC83xx_IRQ_IRQ5
+#define PIRQC        MPC83xx_IRQ_IRQ6
+#define PIRQD        MPC83xx_IRQ_IRQ7
+
+#define MPC834x_SYS_PCI1_LOWER_IO        0x00000000
+#define MPC834x_SYS_PCI1_UPPER_IO        0x00ffffff
+
+#define MPC834x_SYS_PCI1_LOWER_MEM       0x80000000
+#define MPC834x_SYS_PCI1_UPPER_MEM       0x9fffffff
+
+#define MPC834x_SYS_PCI1_IO_BASE         0xe2000000
+#define MPC834x_SYS_PCI1_MEM_OFFSET      0x00000000
+
+#define MPC834x_SYS_PCI1_IO_SIZE         0x01000000
+#endif /* CONFIG_PCI */
+
+#endif                /* __MACH_MPC83XX_SYS_H__ */
diff --git a/arch/ppc/platforms/chrp_pegasos_eth.c b/arch/ppc/platforms/chrp_pegasos_eth.c
new file mode 100644 (file)
index 0000000..cad5bfa
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *  arch/ppc/platforms/chrp_pegasos_eth.c
+ *
+ *  Copyright (C) 2005 Sven Luther <sl@bplan-gmbh.de>
+ *  Thanks to :
+ *     Dale Farnsworth <dale@farnsworth.org>
+ *     Mark A. Greer <mgreer@mvista.com>
+ *     Nicolas DET <nd@bplan-gmbh.de>
+ *     Benjamin Herrenschmidt <benh@kernel.crashing.org>
+ *  And anyone else who helped me on this.
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/mv643xx.h>
+#include <linux/pci.h>
+
+/* Pegasos 2 specific Marvell MV 64361 gigabit ethernet port setup */
+static struct resource mv643xx_eth_shared_resources[] = {
+       [0] = {
+               .name   = "ethernet shared base",
+               .start  = 0xf1000000 + MV643XX_ETH_SHARED_REGS,
+               .end    = 0xf1000000 + MV643XX_ETH_SHARED_REGS +
+                                       MV643XX_ETH_SHARED_REGS_SIZE - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device mv643xx_eth_shared_device = {
+       .name           = MV643XX_ETH_SHARED_NAME,
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(mv643xx_eth_shared_resources),
+       .resource       = mv643xx_eth_shared_resources,
+};
+
+static struct resource mv643xx_eth0_resources[] = {
+       [0] = {
+               .name   = "eth0 irq",
+               .start  = 9,
+               .end    = 9,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct mv643xx_eth_platform_data eth0_pd;
+
+static struct platform_device eth0_device = {
+       .name           = MV643XX_ETH_NAME,
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(mv643xx_eth0_resources),
+       .resource       = mv643xx_eth0_resources,
+       .dev = {
+               .platform_data = &eth0_pd,
+       },
+};
+
+static struct resource mv643xx_eth1_resources[] = {
+       [0] = {
+               .name   = "eth1 irq",
+               .start  = 9,
+               .end    = 9,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct mv643xx_eth_platform_data eth1_pd;
+
+static struct platform_device eth1_device = {
+       .name           = MV643XX_ETH_NAME,
+       .id             = 1,
+       .num_resources  = ARRAY_SIZE(mv643xx_eth1_resources),
+       .resource       = mv643xx_eth1_resources,
+       .dev = {
+               .platform_data = &eth1_pd,
+       },
+};
+
+static struct platform_device *mv643xx_eth_pd_devs[] __initdata = {
+       &mv643xx_eth_shared_device,
+       &eth0_device,
+       &eth1_device,
+};
+
+
+int
+mv643xx_eth_add_pds(void)
+{
+       int ret = 0;
+       static struct pci_device_id pci_marvell_mv64360[] = {
+               { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_MV64360) },
+               { }
+       };
+
+       if (pci_dev_present(pci_marvell_mv64360)) {
+               ret = platform_add_devices(mv643xx_eth_pd_devs, ARRAY_SIZE(mv643xx_eth_pd_devs));
+       }
+       return ret;
+}
+device_initcall(mv643xx_eth_add_pds);
diff --git a/arch/ppc/platforms/hdpu.c b/arch/ppc/platforms/hdpu.c
new file mode 100644 (file)
index 0000000..b659d7b
--- /dev/null
@@ -0,0 +1,1062 @@
+
+/*
+ * arch/ppc/platforms/hdpu_setup.c
+ *
+ * Board setup routines for the Sky Computers HDPU Compute Blade.
+ *
+ * Written by Brian Waite <waite@skycomputers.com>
+ *
+ * Based on code done by - Mark A. Greer <mgreer@mvista.com>
+ *                         Rabeeh Khoury - rabeeh@galileo.co.il
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the 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/pci.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/ide.h>
+#include <linux/seq_file.h>
+
+#include <linux/initrd.h>
+#include <linux/root_dev.h>
+#include <linux/smp.h>
+
+#include <asm/time.h>
+#include <asm/machdep.h>
+#include <asm/todc.h>
+#include <asm/mv64x60.h>
+#include <asm/ppcboot.h>
+#include <platforms/hdpu.h>
+#include <linux/mv643xx.h>
+#include <linux/hdpu_features.h>
+#include <linux/device.h>
+#include <linux/mtd/physmap.h>
+
+#define BOARD_VENDOR   "Sky Computers"
+#define BOARD_MACHINE  "HDPU-CB-A"
+
+bd_t ppcboot_bd;
+int ppcboot_bd_valid = 0;
+
+static mv64x60_handle_t bh;
+
+extern char cmd_line[];
+
+unsigned long hdpu_find_end_of_memory(void);
+void hdpu_mpsc_progress(char *s, unsigned short hex);
+void hdpu_heartbeat(void);
+
+static void parse_bootinfo(unsigned long r3,
+                          unsigned long r4, unsigned long r5,
+                          unsigned long r6, unsigned long r7);
+static void hdpu_set_l1pe(void);
+static void hdpu_cpustate_set(unsigned char new_state);
+#ifdef CONFIG_SMP
+static spinlock_t timebase_lock = SPIN_LOCK_UNLOCKED;
+static unsigned int timebase_upper = 0, timebase_lower = 0;
+extern int smp_tb_synchronized;
+
+void __devinit hdpu_tben_give(void);
+void __devinit hdpu_tben_take(void);
+#endif
+
+static int __init
+hdpu_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] = {
+                       {HDPU_PCI_0_IRQ, 0, 0, 0},
+                       {HDPU_PCI_0_IRQ, 0, 0, 0},
+               };
+
+               const long min_idsel = 1, max_idsel = 2, irqs_per_slot = 4;
+               return PCI_IRQ_TABLE_LOOKUP;
+       } else {
+               static char pci_irq_table[][4] = {
+                       {HDPU_PCI_1_IRQ, 0, 0, 0},
+               };
+
+               const long min_idsel = 1, max_idsel = 1, irqs_per_slot = 4;
+               return PCI_IRQ_TABLE_LOOKUP;
+       }
+}
+
+static void __init hdpu_intr_setup(void)
+{
+       mv64x60_write(&bh, MV64x60_GPP_IO_CNTL,
+                     (1 | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) |
+                      (1 << 6) | (1 << 7) | (1 << 12) | (1 << 16) |
+                      (1 << 18) | (1 << 19) | (1 << 20) | (1 << 21) |
+                      (1 << 22) | (1 << 23) | (1 << 24) | (1 << 25) |
+                      (1 << 26) | (1 << 27) | (1 << 28) | (1 << 29)));
+
+       /* XXXX 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, ~((1 << 8) | (1 << 13)));
+       mv64x60_set_bits(&bh, MV64x60_GPP_INTR_MASK, (1 << 8) | (1 << 13));
+
+       /*
+        * 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));
+}
+
+static void __init hdpu_setup_peripherals(void)
+{
+       unsigned int val;
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN,
+                                HDPU_EMB_FLASH_BASE, HDPU_EMB_FLASH_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN);
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_0_WIN,
+                                HDPU_TBEN_BASE, HDPU_TBEN_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_0_WIN);
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_1_WIN,
+                                HDPU_NEXUS_ID_BASE, HDPU_NEXUS_ID_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_1_WIN);
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2SRAM_WIN,
+                                HDPU_INTERNAL_SRAM_BASE,
+                                HDPU_INTERNAL_SRAM_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN);
+
+       bh.ci->disable_window_32bit(&bh, MV64x60_ENET2MEM_4_WIN);
+       mv64x60_set_32bit_window(&bh, MV64x60_ENET2MEM_4_WIN, 0, 0, 0);
+
+       mv64x60_clr_bits(&bh, MV64x60_PCI0_PCI_DECODE_CNTL, (1 << 3));
+       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)));
+
+       /* Enable pipelining */
+       mv64x60_set_bits(&bh, MV64x60_CPU_CONFIG, (1 << 13));
+       /* Enable Snoop Pipelineing */
+       mv64x60_set_bits(&bh, MV64360_D_UNIT_CONTROL_HIGH, (1 << 24));
+
+       /*
+        * Change DRAM read buffer assignment.
+        * Assign read buffer 0 dedicated only for CPU,
+        * and the rest read buffer 1.
+        */
+       val = mv64x60_read(&bh, MV64360_SDRAM_CONFIG);
+       val = val & 0x03ffffff;
+       val = val | 0xf8000000;
+       mv64x60_write(&bh, MV64360_SDRAM_CONFIG, val);
+
+       /*
+        * Configure internal SRAM -
+        * Cache coherent write back, if 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
+
+       hdpu_intr_setup();
+}
+
+static void __init hdpu_setup_bridge(void)
+{
+       struct mv64x60_setup_info si;
+       int i;
+
+       memset(&si, 0, sizeof(si));
+
+       si.phys_reg_base = HDPU_BRIDGE_REG_BASE;
+       si.pci_0.enable_bus = 1;
+       si.pci_0.pci_io.cpu_base = HDPU_PCI0_IO_START_PROC_ADDR;
+       si.pci_0.pci_io.pci_base_hi = 0;
+       si.pci_0.pci_io.pci_base_lo = HDPU_PCI0_IO_START_PCI_ADDR;
+       si.pci_0.pci_io.size = HDPU_PCI0_IO_SIZE;
+       si.pci_0.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE;
+       si.pci_0.pci_mem[0].cpu_base = HDPU_PCI0_MEM_START_PROC_ADDR;
+       si.pci_0.pci_mem[0].pci_base_hi = HDPU_PCI0_MEM_START_PCI_HI_ADDR;
+       si.pci_0.pci_mem[0].pci_base_lo = HDPU_PCI0_MEM_START_PCI_LO_ADDR;
+       si.pci_0.pci_mem[0].size = HDPU_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 = HDPU_PCI1_IO_START_PROC_ADDR;
+       si.pci_1.pci_io.pci_base_hi = 0;
+       si.pci_1.pci_io.pci_base_lo = HDPU_PCI1_IO_START_PCI_ADDR;
+       si.pci_1.pci_io.size = HDPU_PCI1_IO_SIZE;
+       si.pci_1.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE;
+       si.pci_1.pci_mem[0].cpu_base = HDPU_PCI1_MEM_START_PROC_ADDR;
+       si.pci_1.pci_mem[0].pci_base_hi = HDPU_PCI1_MEM_START_PCI_HI_ADDR;
+       si.pci_1.pci_mem[0].pci_base_lo = HDPU_PCI1_MEM_START_PCI_LO_ADDR;
+       si.pci_1.pci_mem[0].size = HDPU_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;
+
+               si.pci_0.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_WB; /* errata */
+               si.mpsc_options[i] = MV64360_MPSC2MEM_SNOOP_WB; /* errata */
+               si.idma_options[i] = MV64360_IDMA2MEM_SNOOP_WB; /* errata */
+
+               si.pci_0.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_256_BYTES;
+
+               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_256_BYTES;
+#endif
+       }
+
+       hdpu_cpustate_set(CPUSTATE_KERNEL_MAJOR | CPUSTATE_KERNEL_INIT_PCI);
+
+       /* Lookup PCI host bridges */
+       mv64x60_init(&bh, &si);
+       pci_dram_offset = 0;    /* System mem at same addr on PCI & cpu bus */
+       ppc_md.pci_swizzle = common_swizzle;
+       ppc_md.pci_map_irq = hdpu_map_irq;
+
+       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);
+
+       ppc_md.pci_exclude_device = mv64x60_pci_exclude_device;
+
+       hdpu_cpustate_set(CPUSTATE_KERNEL_MAJOR | CPUSTATE_KERNEL_INIT_REG);
+       /*
+        * Enabling of PCI internal-vs-external arbitration
+        * is a platform- and errata-dependent decision.
+        */
+       return;
+}
+
+#if defined(CONFIG_SERIAL_MPSC_CONSOLE)
+static void __init hdpu_early_serial_map(void)
+{
+#ifdef CONFIG_KGDB
+       static char first_time = 1;
+
+#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
+
+       if (first_time) {
+               gt_early_mpsc_init(KGDB_PORT,
+                                  B9600 | CS8 | CREAD | HUPCL | CLOCAL);
+               first_time = 0;
+       }
+
+       return;
+#endif
+}
+#endif
+
+static void hdpu_init2(void)
+{
+       return;
+}
+
+#if defined(CONFIG_MV643XX_ETH)
+static void __init hdpu_fixup_eth_pdata(struct platform_device *pd)
+{
+
+       struct mv643xx_eth_platform_data *eth_pd;
+       eth_pd = pd->dev.platform_data;
+
+       eth_pd->port_serial_control =
+           mv64x60_read(&bh, MV643XX_ETH_PORT_SERIAL_CONTROL_REG(pd->id) & ~1);
+
+       eth_pd->force_phy_addr = 1;
+       eth_pd->phy_addr = pd->id;
+       eth_pd->tx_queue_size = 400;
+       eth_pd->rx_queue_size = 800;
+}
+#endif
+
+static void __init hdpu_fixup_mpsc_pdata(struct platform_device *pd)
+{
+
+       struct mpsc_pdata *pdata;
+
+       pdata = (struct mpsc_pdata *)pd->dev.platform_data;
+
+       pdata->max_idle = 40;
+       if (ppcboot_bd_valid)
+               pdata->default_baud = ppcboot_bd.bi_baudrate;
+       else
+               pdata->default_baud = HDPU_DEFAULT_BAUD;
+       pdata->brg_clk_src = HDPU_MPSC_CLK_SRC;
+       pdata->brg_clk_freq = HDPU_MPSC_CLK_FREQ;
+}
+
+#if defined(CONFIG_HDPU_FEATURES)
+static void __init hdpu_fixup_cpustate_pdata(struct platform_device *pd)
+{
+       struct platform_device *pds[1];
+       pds[0] = pd;
+       mv64x60_pd_fixup(&bh, pds, 1);
+}
+#endif
+
+static int __init hdpu_platform_notify(struct device *dev)
+{
+       static struct {
+               char *bus_id;
+               void ((*rtn) (struct platform_device * pdev));
+       } dev_map[] = {
+               {
+               MPSC_CTLR_NAME ".0", hdpu_fixup_mpsc_pdata},
+#if defined(CONFIG_MV643XX_ETH)
+               {
+               MV643XX_ETH_NAME ".0", hdpu_fixup_eth_pdata},
+#endif
+#if defined(CONFIG_HDPU_FEATURES)
+               {
+               HDPU_CPUSTATE_NAME ".0", hdpu_fixup_cpustate_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 __init hdpu_setup_arch(void)
+{
+       if (ppc_md.progress)
+               ppc_md.progress("hdpu_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
+
+       ppc_md.heartbeat = hdpu_heartbeat;
+
+       ppc_md.heartbeat_reset = HZ;
+       ppc_md.heartbeat_count = 1;
+
+       if (ppc_md.progress)
+               ppc_md.progress("hdpu_setup_arch: Enabling L2 cache", 0);
+
+       /* Enable L1 Parity Bits */
+       hdpu_set_l1pe();
+
+       /* Enable L2 and L3 caches (if 745x) */
+       _set_L2CR(0x80080000);
+
+       if (ppc_md.progress)
+               ppc_md.progress("hdpu_setup_arch: enter", 0);
+
+       hdpu_setup_bridge();
+
+       hdpu_setup_peripherals();
+
+#ifdef CONFIG_SERIAL_MPSC_CONSOLE
+       hdpu_early_serial_map();
+#endif
+
+       printk("SKY HDPU Compute Blade \n");
+
+       if (ppc_md.progress)
+               ppc_md.progress("hdpu_setup_arch: exit", 0);
+
+       hdpu_cpustate_set(CPUSTATE_KERNEL_MAJOR | CPUSTATE_KERNEL_OK);
+       return;
+}
+static void __init hdpu_init_irq(void)
+{
+       mv64360_init_irq();
+}
+
+static void __init hdpu_set_l1pe()
+{
+       unsigned long ictrl;
+       asm volatile ("mfspr %0, 1011":"=r" (ictrl):);
+       ictrl |= ICTRL_EICE | ICTRL_EDC | ICTRL_EICP;
+       asm volatile ("mtspr 1011, %0"::"r" (ictrl));
+}
+
+/*
+ * Set BAT 1 to map 0xf1000000 to end of physical memory space.
+ */
+static __inline__ void hdpu_set_bat(void)
+{
+       mb();
+       mtspr(SPRN_DBAT1U, 0xf10001fe);
+       mtspr(SPRN_DBAT1L, 0xf100002a);
+       mb();
+
+       return;
+}
+
+unsigned long __init hdpu_find_end_of_memory(void)
+{
+       return mv64x60_get_mem_size(CONFIG_MV64X60_NEW_BASE,
+                                   MV64x60_TYPE_MV64360);
+}
+
+static void hdpu_reset_board(void)
+{
+       volatile int infinite = 1;
+
+       hdpu_cpustate_set(CPUSTATE_KERNEL_MAJOR | CPUSTATE_KERNEL_RESET);
+
+       local_irq_disable();
+
+       /* Clear all the LEDs */
+       mv64x60_write(&bh, MV64x60_GPP_VALUE_CLR, ((1 << 4) |
+                                                  (1 << 5) | (1 << 6)));
+
+       /* disable and invalidate the L2 cache */
+       _set_L2CR(0);
+       _set_L2CR(0x200000);
+
+       /* flush and disable L1 I/D cache */
+       __asm__ __volatile__
+           ("\n"
+            "mfspr   3,1008\n"
+            "ori       5,5,0xcc00\n"
+            "ori       4,3,0xc00\n"
+            "andc      5,3,5\n"
+            "sync\n"
+            "mtspr     1008,4\n"
+            "isync\n" "sync\n" "mtspr  1008,5\n" "isync\n" "sync\n");
+
+       /* Hit the reset bit */
+       mv64x60_write(&bh, MV64x60_GPP_VALUE_CLR, (1 << 3));
+
+       while (infinite)
+               infinite = infinite;
+
+       return;
+}
+
+static void hdpu_restart(char *cmd)
+{
+       volatile ulong i = 10000000;
+
+       hdpu_reset_board();
+
+       while (i-- > 0) ;
+       panic("restart failed\n");
+}
+
+static void hdpu_halt(void)
+{
+       local_irq_disable();
+
+       hdpu_cpustate_set(CPUSTATE_KERNEL_MAJOR | CPUSTATE_KERNEL_HALT);
+
+       /* Clear all the LEDs */
+       mv64x60_write(&bh, MV64x60_GPP_VALUE_CLR, ((1 << 4) | (1 << 5) |
+                                                  (1 << 6)));
+       while (1) ;
+       /* NOTREACHED */
+}
+
+static void hdpu_power_off(void)
+{
+       hdpu_halt();
+       /* NOTREACHED */
+}
+
+static int hdpu_show_cpuinfo(struct seq_file *m)
+{
+       uint pvid;
+
+       pvid = mfspr(SPRN_PVR);
+       seq_printf(m, "vendor\t\t: Sky Computers\n");
+       seq_printf(m, "machine\t\t: HDPU Compute Blade\n");
+       seq_printf(m, "PVID\t\t: 0x%x, vendor: %s\n",
+                  pvid, (pvid & (1 << 15) ? "IBM" : "Motorola"));
+
+       return 0;
+}
+
+static void __init hdpu_calibrate_decr(void)
+{
+       ulong freq;
+
+       if (ppcboot_bd_valid)
+               freq = ppcboot_bd.bi_busfreq / 4;
+       else
+               freq = 133000000;
+
+       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 void parse_bootinfo(unsigned long r3,
+                          unsigned long r4, unsigned long r5,
+                          unsigned long r6, unsigned long r7)
+{
+       bd_t *bd = NULL;
+       char *cmdline_start = NULL;
+       int cmdline_len = 0;
+
+       if (r3) {
+               if ((r3 & 0xf0000000) == 0)
+                       r3 += KERNELBASE;
+               if ((r3 & 0xf0000000) == KERNELBASE) {
+                       bd = (void *)r3;
+
+                       memcpy(&ppcboot_bd, bd, sizeof(ppcboot_bd));
+                       ppcboot_bd_valid = 1;
+               }
+       }
+#ifdef CONFIG_BLK_DEV_INITRD
+       if (r4 && r5 && r5 > r4) {
+               if ((r4 & 0xf0000000) == 0)
+                       r4 += KERNELBASE;
+               if ((r5 & 0xf0000000) == 0)
+                       r5 += KERNELBASE;
+               if ((r4 & 0xf0000000) == KERNELBASE) {
+                       initrd_start = r4;
+                       initrd_end = r5;
+                       initrd_below_start_ok = 1;
+               }
+       }
+#endif                         /* CONFIG_BLK_DEV_INITRD */
+
+       if (r6 && r7 && r7 > r6) {
+               if ((r6 & 0xf0000000) == 0)
+                       r6 += KERNELBASE;
+               if ((r7 & 0xf0000000) == 0)
+                       r7 += KERNELBASE;
+               if ((r6 & 0xf0000000) == KERNELBASE) {
+                       cmdline_start = (void *)r6;
+                       cmdline_len = (r7 - r6);
+                       strncpy(cmd_line, cmdline_start, cmdline_len);
+               }
+       }
+}
+
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+static int hdpu_ide_check_region(ide_ioreg_t from, unsigned int extent)
+{
+       return check_region(from, extent);
+}
+
+static void
+hdpu_ide_request_region(ide_ioreg_t from, unsigned int extent, const char *name)
+{
+       request_region(from, extent, name);
+       return;
+}
+
+static void hdpu_ide_release_region(ide_ioreg_t from, unsigned int extent)
+{
+       release_region(from, extent);
+       return;
+}
+
+static void __init
+hdpu_ide_pci_init_hwif_ports(hw_regs_t * hw, ide_ioreg_t data_port,
+                            ide_ioreg_t ctrl_port, int *irq)
+{
+       struct pci_dev *dev;
+
+       pci_for_each_dev(dev) {
+               if (((dev->class >> 8) == PCI_CLASS_STORAGE_IDE) ||
+                   ((dev->class >> 8) == PCI_CLASS_STORAGE_RAID)) {
+                       hw->irq = dev->irq;
+
+                       if (irq != NULL) {
+                               *irq = dev->irq;
+                       }
+               }
+       }
+
+       return;
+}
+#endif
+
+void hdpu_heartbeat(void)
+{
+       if (mv64x60_read(&bh, MV64x60_GPP_VALUE) & (1 << 5))
+               mv64x60_write(&bh, MV64x60_GPP_VALUE_CLR, (1 << 5));
+       else
+               mv64x60_write(&bh, MV64x60_GPP_VALUE_SET, (1 << 5));
+
+       ppc_md.heartbeat_count = ppc_md.heartbeat_reset;
+
+}
+
+static void __init hdpu_map_io(void)
+{
+       io_block_mapping(0xf1000000, 0xf1000000, 0x20000, _PAGE_IO);
+}
+
+#ifdef CONFIG_SMP
+char hdpu_smp0[] = "SMP Cpu #0";
+char hdpu_smp1[] = "SMP Cpu #1";
+
+static irqreturn_t hdpu_smp_cpu0_int_handler(int irq, void *dev_id,
+                                            struct pt_regs *regs)
+{
+       volatile unsigned int doorbell;
+
+       doorbell = mv64x60_read(&bh, MV64360_CPU0_DOORBELL);
+
+       /* Ack the doorbell interrupts */
+       mv64x60_write(&bh, MV64360_CPU0_DOORBELL_CLR, doorbell);
+
+       if (doorbell & 1) {
+               smp_message_recv(0, regs);
+       }
+       if (doorbell & 2) {
+               smp_message_recv(1, regs);
+       }
+       if (doorbell & 4) {
+               smp_message_recv(2, regs);
+       }
+       if (doorbell & 8) {
+               smp_message_recv(3, regs);
+       }
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t hdpu_smp_cpu1_int_handler(int irq, void *dev_id,
+                                            struct pt_regs *regs)
+{
+       volatile unsigned int doorbell;
+
+       doorbell = mv64x60_read(&bh, MV64360_CPU1_DOORBELL);
+
+       /* Ack the doorbell interrupts */
+       mv64x60_write(&bh, MV64360_CPU1_DOORBELL_CLR, doorbell);
+
+       if (doorbell & 1) {
+               smp_message_recv(0, regs);
+       }
+       if (doorbell & 2) {
+               smp_message_recv(1, regs);
+       }
+       if (doorbell & 4) {
+               smp_message_recv(2, regs);
+       }
+       if (doorbell & 8) {
+               smp_message_recv(3, regs);
+       }
+       return IRQ_HANDLED;
+}
+
+static void smp_hdpu_CPU_two(void)
+{
+       __asm__ __volatile__
+           ("\n"
+            "lis     3,0x0000\n"
+            "ori     3,3,0x00c0\n"
+            "mtspr   26, 3\n" "li      4,0\n" "mtspr   27,4\n" "rfi");
+
+}
+
+static int smp_hdpu_probe(void)
+{
+       int *cpu_count_reg;
+       int num_cpus = 0;
+
+       cpu_count_reg = ioremap(HDPU_NEXUS_ID_BASE, HDPU_NEXUS_ID_SIZE);
+       if (cpu_count_reg) {
+               num_cpus = (*cpu_count_reg >> 20) & 0x3;
+               iounmap(cpu_count_reg);
+       }
+
+       /* Validate the bits in the CPLD. If we could not map the reg, return 2.
+        * If the register reported 0 or 3, return 2.
+        * Older CPLD revisions set these bits to all ones (val = 3).
+        */
+       if ((num_cpus < 1) || (num_cpus > 2)) {
+               printk
+                   ("Unable to determine the number of processors %d . deafulting to 2.\n",
+                    num_cpus);
+               num_cpus = 2;
+       }
+       return num_cpus;
+}
+
+static void
+smp_hdpu_message_pass(int target, int msg, unsigned long data, int wait)
+{
+       if (msg > 0x3) {
+               printk("SMP %d: smp_message_pass: unknown msg %d\n",
+                      smp_processor_id(), msg);
+               return;
+       }
+       switch (target) {
+       case MSG_ALL:
+               mv64x60_write(&bh, MV64360_CPU0_DOORBELL, 1 << msg);
+               mv64x60_write(&bh, MV64360_CPU1_DOORBELL, 1 << msg);
+               break;
+       case MSG_ALL_BUT_SELF:
+               if (smp_processor_id())
+                       mv64x60_write(&bh, MV64360_CPU0_DOORBELL, 1 << msg);
+               else
+                       mv64x60_write(&bh, MV64360_CPU1_DOORBELL, 1 << msg);
+               break;
+       default:
+               if (target == 0)
+                       mv64x60_write(&bh, MV64360_CPU0_DOORBELL, 1 << msg);
+               else
+                       mv64x60_write(&bh, MV64360_CPU1_DOORBELL, 1 << msg);
+               break;
+       }
+}
+
+static void smp_hdpu_kick_cpu(int nr)
+{
+       volatile unsigned int *bootaddr;
+
+       if (ppc_md.progress)
+               ppc_md.progress("smp_hdpu_kick_cpu", 0);
+
+       hdpu_cpustate_set(CPUSTATE_KERNEL_MAJOR | CPUSTATE_KERNEL_CPU1_KICK);
+
+       /* Disable BootCS. Must also reduce the windows size to zero. */
+       bh.ci->disable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN);
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN, 0, 0, 0);
+
+       bootaddr = ioremap(HDPU_INTERNAL_SRAM_BASE, HDPU_INTERNAL_SRAM_SIZE);
+       if (!bootaddr) {
+               if (ppc_md.progress)
+                       ppc_md.progress("smp_hdpu_kick_cpu: ioremap failed", 0);
+               return;
+       }
+
+       memcpy((void *)(bootaddr + 0x40), (void *)&smp_hdpu_CPU_two, 0x20);
+
+       /* map SRAM to 0xfff00000 */
+       bh.ci->disable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN);
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2SRAM_WIN,
+                                0xfff00000, HDPU_INTERNAL_SRAM_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN);
+
+       /* Enable CPU1 arbitration */
+       mv64x60_clr_bits(&bh, MV64x60_CPU_MASTER_CNTL, (1 << 9));
+
+       /*
+        * Wait 100mSecond until other CPU has reached __secondary_start.
+        * When it reaches, it is permittable to rever the SRAM mapping etc...
+        */
+       mdelay(100);
+       *(unsigned long *)KERNELBASE = nr;
+       asm volatile ("dcbf 0,%0"::"r" (KERNELBASE):"memory");
+
+       iounmap(bootaddr);
+
+       /* Set up window for internal sram (256KByte insize) */
+       bh.ci->disable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN);
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2SRAM_WIN,
+                                HDPU_INTERNAL_SRAM_BASE,
+                                HDPU_INTERNAL_SRAM_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN);
+       /*
+        * Set up windows for embedded FLASH (using boot CS window).
+        */
+
+       bh.ci->disable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN);
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN,
+                                HDPU_EMB_FLASH_BASE, HDPU_EMB_FLASH_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN);
+}
+
+static void smp_hdpu_setup_cpu(int cpu_nr)
+{
+       if (cpu_nr == 0) {
+               if (ppc_md.progress)
+                       ppc_md.progress("smp_hdpu_setup_cpu 0", 0);
+               mv64x60_write(&bh, MV64360_CPU0_DOORBELL_CLR, 0xff);
+               mv64x60_write(&bh, MV64360_CPU0_DOORBELL_MASK, 0xff);
+               request_irq(60, hdpu_smp_cpu0_int_handler,
+                           SA_INTERRUPT, hdpu_smp0, 0);
+       }
+
+       if (cpu_nr == 1) {
+               if (ppc_md.progress)
+                       ppc_md.progress("smp_hdpu_setup_cpu 1", 0);
+
+               hdpu_cpustate_set(CPUSTATE_KERNEL_MAJOR |
+                                 CPUSTATE_KERNEL_CPU1_OK);
+
+               /* Enable L1 Parity Bits */
+               hdpu_set_l1pe();
+
+               /* Enable L2 cache */
+               _set_L2CR(0);
+               _set_L2CR(0x80080000);
+
+               mv64x60_write(&bh, MV64360_CPU1_DOORBELL_CLR, 0x0);
+               mv64x60_write(&bh, MV64360_CPU1_DOORBELL_MASK, 0xff);
+               request_irq(28, hdpu_smp_cpu1_int_handler,
+                           SA_INTERRUPT, hdpu_smp1, 0);
+       }
+
+}
+
+void __devinit hdpu_tben_give()
+{
+       volatile unsigned long *val = 0;
+
+       /* By writing 0 to the TBEN_BASE, the timebases is frozen */
+       val = ioremap(HDPU_TBEN_BASE, 4);
+       *val = 0;
+       mb();
+
+       spin_lock(&timebase_lock);
+       timebase_upper = get_tbu();
+       timebase_lower = get_tbl();
+       spin_unlock(&timebase_lock);
+
+       while (timebase_upper || timebase_lower)
+               barrier();
+
+       /* By writing 1 to the TBEN_BASE, the timebases is thawed */
+       *val = 1;
+       mb();
+
+       iounmap(val);
+
+}
+
+void __devinit hdpu_tben_take()
+{
+       while (!(timebase_upper || timebase_lower))
+               barrier();
+
+       spin_lock(&timebase_lock);
+       set_tb(timebase_upper, timebase_lower);
+       timebase_upper = 0;
+       timebase_lower = 0;
+       spin_unlock(&timebase_lock);
+}
+
+static struct smp_ops_t hdpu_smp_ops = {
+       .message_pass = smp_hdpu_message_pass,
+       .probe = smp_hdpu_probe,
+       .kick_cpu = smp_hdpu_kick_cpu,
+       .setup_cpu = smp_hdpu_setup_cpu,
+       .give_timebase = hdpu_tben_give,
+       .take_timebase = hdpu_tben_take,
+};
+#endif                         /* CONFIG_SMP */
+
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+             unsigned long r6, unsigned long r7)
+{
+       parse_bootinfo(r3, r4, r5, r6, r7);
+
+       isa_mem_base = 0;
+
+       ppc_md.setup_arch = hdpu_setup_arch;
+       ppc_md.init = hdpu_init2;
+       ppc_md.show_cpuinfo = hdpu_show_cpuinfo;
+       ppc_md.init_IRQ = hdpu_init_irq;
+       ppc_md.get_irq = mv64360_get_irq;
+       ppc_md.restart = hdpu_restart;
+       ppc_md.power_off = hdpu_power_off;
+       ppc_md.halt = hdpu_halt;
+       ppc_md.find_end_of_memory = hdpu_find_end_of_memory;
+       ppc_md.calibrate_decr = hdpu_calibrate_decr;
+       ppc_md.setup_io_mappings = hdpu_map_io;
+
+       bh.p_base = CONFIG_MV64X60_NEW_BASE;
+       bh.v_base = (unsigned long *)bh.p_base;
+
+       hdpu_set_bat();
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG)
+       ppc_md.progress = hdpu_mpsc_progress;   /* embedded UART */
+       mv64x60_progress_init(bh.p_base);
+#endif                         /* CONFIG_SERIAL_TEXT_DEBUG */
+
+#ifdef CONFIG_SMP
+       ppc_md.smp_ops = &hdpu_smp_ops;
+#endif                         /* CONFIG_SMP */
+
+#if defined(CONFIG_SERIAL_MPSC) || defined(CONFIG_MV643XX_ETH)
+       platform_notify = hdpu_platform_notify;
+#endif
+       return;
+}
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) && defined(CONFIG_SERIAL_MPSC_CONSOLE)
+/* SMP safe version of the serial text debug routine. Uses Semaphore 0 */
+void hdpu_mpsc_progress(char *s, unsigned short hex)
+{
+       while (mv64x60_read(&bh, MV64360_WHO_AM_I) !=
+              mv64x60_read(&bh, MV64360_SEMAPHORE_0)) {
+       }
+       mv64x60_mpsc_progress(s, hex);
+       mv64x60_write(&bh, MV64360_SEMAPHORE_0, 0xff);
+}
+#endif
+
+static void hdpu_cpustate_set(unsigned char new_state)
+{
+       unsigned int state = (new_state << 21);
+       mv64x60_write(&bh, MV64x60_GPP_VALUE_CLR, (0xff << 21));
+       mv64x60_write(&bh, MV64x60_GPP_VALUE_CLR, state);
+}
+
+#ifdef CONFIG_MTD_PHYSMAP
+static struct mtd_partition hdpu_partitions[] = {
+       {
+        .name = "Root FS",
+        .size = 0x03400000,
+        .offset = 0,
+        .mask_flags = 0,
+        },{
+        .name = "User FS",
+        .size = 0x00800000,
+        .offset = 0x03400000,
+        .mask_flags = 0,
+        },{
+        .name = "Kernel Image",
+        .size = 0x002C0000,
+        .offset = 0x03C00000,
+        .mask_flags = 0,
+        },{
+        .name = "bootEnv",
+        .size = 0x00040000,
+        .offset = 0x03EC0000,
+        .mask_flags = 0,
+        },{
+        .name = "bootROM",
+        .size = 0x00100000,
+        .offset = 0x03F00000,
+        .mask_flags = 0,
+        }
+};
+
+static int __init hdpu_setup_mtd(void)
+{
+
+       physmap_set_partitions(hdpu_partitions, 5);
+       return 0;
+}
+
+arch_initcall(hdpu_setup_mtd);
+#endif
+
+#ifdef CONFIG_HDPU_FEATURES
+
+static struct resource hdpu_cpustate_resources[] = {
+       [0] = {
+              .name = "addr base",
+              .start = MV64x60_GPP_VALUE_SET,
+              .end = MV64x60_GPP_VALUE_CLR + 1,
+              .flags = IORESOURCE_MEM,
+              },
+};
+
+static struct resource hdpu_nexus_resources[] = {
+       [0] = {
+              .name = "nexus register",
+              .start = HDPU_NEXUS_ID_BASE,
+              .end = HDPU_NEXUS_ID_BASE + HDPU_NEXUS_ID_SIZE,
+              .flags = IORESOURCE_MEM,
+              },
+};
+
+static struct platform_device hdpu_cpustate_device = {
+       .name = HDPU_CPUSTATE_NAME,
+       .id = 0,
+       .num_resources = ARRAY_SIZE(hdpu_cpustate_resources),
+       .resource = hdpu_cpustate_resources,
+};
+
+static struct platform_device hdpu_nexus_device = {
+       .name = HDPU_NEXUS_NAME,
+       .id = 0,
+       .num_resources = ARRAY_SIZE(hdpu_nexus_resources),
+       .resource = hdpu_nexus_resources,
+};
+
+static int __init hdpu_add_pds(void)
+{
+       platform_device_register(&hdpu_cpustate_device);
+       platform_device_register(&hdpu_nexus_device);
+       return 0;
+}
+
+arch_initcall(hdpu_add_pds);
+#endif
diff --git a/arch/ppc/platforms/hdpu.h b/arch/ppc/platforms/hdpu.h
new file mode 100644 (file)
index 0000000..07c3cff
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * arch/ppc/platforms/hdpu.h
+ *
+ * Definitions for Sky Computers HDPU board.
+ *
+ * Brian Waite <waite@skycomputers.com>
+ *
+ * Based on code done by Rabeeh Khoury - rabeeh@galileo.co.il
+ * Based on code done by Mark A. Greer <mgreer@mvista.com>
+ * Based on code done by  Tim Montgomery <timm@artesyncp.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 64K and start
+ * on a boundary that is a multiple of the window size):
+ *
+ *    0x80000000-0x8fffffff     - PCI 0 MEM
+ *    0xa0000000-0xafffffff     - PCI 1 MEM
+ *    0xc0000000-0xc0ffffff     - PCI 0 I/O
+ *    0xc1000000-0xc1ffffff     - PCI 1 I/O
+
+ *    0xf1000000-0xf100ffff      - MV64360 Registers
+ *    0xf1010000-0xfb9fffff      - HOLE
+ *    0xfbfa0000-0xfbfaffff      - TBEN
+ *    0xfbf00000-0xfbfbffff      - NEXUS
+ *    0xfbfc0000-0xfbffffff      - Internal SRAM
+ *    0xfc000000-0xffffffff      - Boot window
+ */
+
+#ifndef __PPC_PLATFORMS_HDPU_H
+#define __PPC_PLATFORMS_HDPU_H
+
+/* CPU Physical Memory Map setup. */
+#define        HDPU_BRIDGE_REG_BASE                 0xf1000000
+
+#define HDPU_TBEN_BASE                        0xfbfa0000
+#define HDPU_TBEN_SIZE                        0x00010000
+#define HDPU_NEXUS_ID_BASE                    0xfbfb0000
+#define HDPU_NEXUS_ID_SIZE                    0x00010000
+#define HDPU_INTERNAL_SRAM_BASE               0xfbfc0000
+#define HDPU_INTERNAL_SRAM_SIZE               0x00040000
+#define        HDPU_EMB_FLASH_BASE                   0xfc000000
+#define        HDPU_EMB_FLASH_SIZE                   0x04000000
+
+/* PCI Mappings */
+
+#define HDPU_PCI0_MEM_START_PROC_ADDR         0x80000000
+#define HDPU_PCI0_MEM_START_PCI_HI_ADDR       0x00000000
+#define HDPU_PCI0_MEM_START_PCI_LO_ADDR       HDPU_PCI0_MEM_START_PROC_ADDR
+#define HDPU_PCI0_MEM_SIZE                    0x10000000
+
+#define HDPU_PCI1_MEM_START_PROC_ADDR         0xc0000000
+#define HDPU_PCI1_MEM_START_PCI_HI_ADDR       0x00000000
+#define HDPU_PCI1_MEM_START_PCI_LO_ADDR       HDPU_PCI1_MEM_START_PROC_ADDR
+#define HDPU_PCI1_MEM_SIZE                    0x20000000
+
+#define HDPU_PCI0_IO_START_PROC_ADDR          0xc0000000
+#define HDPU_PCI0_IO_START_PCI_ADDR           0x00000000
+#define HDPU_PCI0_IO_SIZE                     0x01000000
+
+#define HDPU_PCI1_IO_START_PROC_ADDR          0xc1000000
+#define HDPU_PCI1_IO_START_PCI_ADDR           0x01000000
+#define HDPU_PCI1_IO_SIZE                     0x01000000
+
+#define HDPU_DEFAULT_BAUD 115200
+#define HDPU_MPSC_CLK_SRC 8    /* TCLK */
+#define HDPU_MPSC_CLK_FREQ 133000000   /* 133 Mhz */
+
+#define        HDPU_PCI_0_IRQ          (8+64)
+#define        HDPU_PCI_1_IRQ          (13+64)
+
+#endif                         /* __PPC_PLATFORMS_HDPU_H */
diff --git a/arch/ppc/platforms/radstone_ppc7d.c b/arch/ppc/platforms/radstone_ppc7d.c
new file mode 100644 (file)
index 0000000..c30607a
--- /dev/null
@@ -0,0 +1,1500 @@
+/*
+ * arch/ppc/platforms/radstone_ppc7d.c
+ *
+ * Board setup routines for the Radstone PPC7D boards.
+ *
+ * Author: James Chapman <jchapman@katalix.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.
+ */
+
+/* Radstone PPC7D boards are rugged VME boards with PPC 7447A CPUs,
+ * Discovery-II, dual gigabit ethernet, dual PMC, USB, keyboard/mouse,
+ * 4 serial ports, 2 high speed serial ports (MPSCs) and optional
+ * SCSI / VGA.
+ */
+
+#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/initrd.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/ide.h>
+#include <linux/seq_file.h>
+#include <linux/root_dev.h>
+#include <linux/serial.h>
+#include <linux/tty.h>         /* for linux/serial_core.h */
+#include <linux/serial_core.h>
+#include <linux/mv643xx.h>
+#include <linux/netdevice.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 <asm/machdep.h>
+#include <asm/prom.h>
+#include <asm/smp.h>
+#include <asm/vga.h>
+#include <asm/open_pic.h>
+#include <asm/i8259.h>
+#include <asm/todc.h>
+#include <asm/bootinfo.h>
+#include <asm/mpc10x.h>
+#include <asm/pci-bridge.h>
+#include <asm/mv64x60.h>
+#include <asm/i8259.h>
+
+#include "radstone_ppc7d.h"
+
+#undef DEBUG
+
+#define PPC7D_RST_PIN                  17      /* GPP17 */
+
+extern u32 mv64360_irq_base;
+extern spinlock_t rtc_lock;
+
+static struct mv64x60_handle bh;
+static int ppc7d_has_alma;
+
+extern void gen550_progress(char *, unsigned short);
+extern void gen550_init(int, struct uart_port *);
+
+/* FIXME - move to h file */
+extern int ds1337_do_command(int id, int cmd, void *arg);
+#define DS1337_GET_DATE         0
+#define DS1337_SET_DATE         1
+
+/* residual data */
+unsigned char __res[sizeof(bd_t)];
+
+/*****************************************************************************
+ * Serial port code
+ *****************************************************************************/
+
+#if defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG)
+static void __init ppc7d_early_serial_map(void)
+{
+#if defined(CONFIG_SERIAL_MPSC_CONSOLE)
+       mv64x60_progress_init(CONFIG_MV64X60_NEW_BASE);
+#elif defined(CONFIG_SERIAL_8250)
+       struct uart_port serial_req;
+
+       /* Setup serial port access */
+       memset(&serial_req, 0, sizeof(serial_req));
+       serial_req.uartclk = UART_CLK;
+       serial_req.irq = 4;
+       serial_req.flags = STD_COM_FLAGS;
+       serial_req.iotype = SERIAL_IO_MEM;
+       serial_req.membase = (u_char *) PPC7D_SERIAL_0;
+
+       gen550_init(0, &serial_req);
+       if (early_serial_setup(&serial_req) != 0)
+               printk(KERN_ERR "Early serial init of port 0 failed\n");
+
+       /* Assume early_serial_setup() doesn't modify serial_req */
+       serial_req.line = 1;
+       serial_req.irq = 3;
+       serial_req.membase = (u_char *) PPC7D_SERIAL_1;
+
+       gen550_init(1, &serial_req);
+       if (early_serial_setup(&serial_req) != 0)
+               printk(KERN_ERR "Early serial init of port 1 failed\n");
+#else
+#error CONFIG_KGDB || CONFIG_SERIAL_TEXT_DEBUG has no supported CONFIG_SERIAL_XXX
+#endif
+}
+#endif /* CONFIG_KGDB || CONFIG_SERIAL_TEXT_DEBUG */
+
+/*****************************************************************************
+ * Low-level board support code
+ *****************************************************************************/
+
+static unsigned long __init ppc7d_find_end_of_memory(void)
+{
+       bd_t *bp = (bd_t *) __res;
+
+       if (bp->bi_memsize)
+               return bp->bi_memsize;
+
+       return (256 * 1024 * 1024);
+}
+
+static void __init ppc7d_map_io(void)
+{
+       /* remove temporary mapping */
+       mtspr(SPRN_DBAT3U, 0x00000000);
+       mtspr(SPRN_DBAT3L, 0x00000000);
+
+       io_block_mapping(0xe8000000, 0xe8000000, 0x08000000, _PAGE_IO);
+       io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO);
+}
+
+static void ppc7d_restart(char *cmd)
+{
+       u32 data;
+
+       /* Disable GPP17 interrupt */
+       data = mv64x60_read(&bh, MV64x60_GPP_INTR_MASK);
+       data &= ~(1 << PPC7D_RST_PIN);
+       mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, data);
+
+       /* Configure MPP17 as GPP */
+       data = mv64x60_read(&bh, MV64x60_MPP_CNTL_2);
+       data &= ~(0x0000000f << 4);
+       mv64x60_write(&bh, MV64x60_MPP_CNTL_2, data);
+
+       /* Enable pin GPP17 for output */
+       data = mv64x60_read(&bh, MV64x60_GPP_IO_CNTL);
+       data |= (1 << PPC7D_RST_PIN);
+       mv64x60_write(&bh, MV64x60_GPP_IO_CNTL, data);
+
+       /* Toggle GPP9 pin to reset the board */
+       mv64x60_write(&bh, MV64x60_GPP_VALUE_CLR, 1 << PPC7D_RST_PIN);
+       mv64x60_write(&bh, MV64x60_GPP_VALUE_SET, 1 << PPC7D_RST_PIN);
+
+       for (;;) ;              /* Spin until reset happens */
+       /* NOTREACHED */
+}
+
+static void ppc7d_power_off(void)
+{
+       u32 data;
+
+       local_irq_disable();
+
+       /* Ensure that internal MV643XX watchdog is disabled.
+        * The Disco watchdog uses MPP17 on this hardware.
+        */
+       data = mv64x60_read(&bh, MV64x60_MPP_CNTL_2);
+       data &= ~(0x0000000f << 4);
+       mv64x60_write(&bh, MV64x60_MPP_CNTL_2, data);
+
+       data = mv64x60_read(&bh, MV64x60_WDT_WDC);
+       if (data & 0x80000000) {
+               mv64x60_write(&bh, MV64x60_WDT_WDC, 1 << 24);
+               mv64x60_write(&bh, MV64x60_WDT_WDC, 2 << 24);
+       }
+
+       for (;;) ;              /* No way to shut power off with software */
+       /* NOTREACHED */
+}
+
+static void ppc7d_halt(void)
+{
+       ppc7d_power_off();
+       /* NOTREACHED */
+}
+
+static unsigned long ppc7d_led_no_pulse;
+
+static int __init ppc7d_led_pulse_disable(char *str)
+{
+       ppc7d_led_no_pulse = 1;
+       return 1;
+}
+
+/* This kernel option disables the heartbeat pulsing of a board LED */
+__setup("ledoff", ppc7d_led_pulse_disable);
+
+static void ppc7d_heartbeat(void)
+{
+       u32 data32;
+       u8 data8;
+       static int max706_wdog = 0;
+
+       /* Unfortunately we can't access the LED control registers
+        * during early init because they're on the CPLD which is the
+        * other side of a PCI bridge which goes unreachable during
+        * PCI scan. So write the LEDs only if the MV64360 watchdog is
+        * enabled (i.e. userspace apps are running so kernel is up)..
+        */
+       data32 = mv64x60_read(&bh, MV64x60_WDT_WDC);
+       if (data32 & 0x80000000) {
+               /* Enable MAX706 watchdog if not done already */
+               if (!max706_wdog) {
+                       outb(3, PPC7D_CPLD_RESET);
+                       max706_wdog = 1;
+               }
+
+               /* Hit the MAX706 watchdog */
+               outb(0, PPC7D_CPLD_WATCHDOG_TRIG);
+
+               /* Pulse LED DS219 if not disabled */
+               if (!ppc7d_led_no_pulse) {
+                       static int led_on = 0;
+
+                       data8 = inb(PPC7D_CPLD_LEDS);
+                       if (led_on)
+                               data8 &= ~PPC7D_CPLD_LEDS_DS219_MASK;
+                       else
+                               data8 |= PPC7D_CPLD_LEDS_DS219_MASK;
+
+                       outb(data8, PPC7D_CPLD_LEDS);
+                       led_on = !led_on;
+               }
+       }
+       ppc_md.heartbeat_count = ppc_md.heartbeat_reset;
+}
+
+static int ppc7d_show_cpuinfo(struct seq_file *m)
+{
+       u8 val;
+       u8 val1, val2;
+       static int flash_sizes[4] = { 64, 32, 0, 16 };
+       static int flash_banks[4] = { 4, 3, 2, 1 };
+       static int sdram_bank_sizes[4] = { 128, 256, 512, 1 };
+       int sdram_num_banks = 2;
+       static char *pci_modes[] = { "PCI33", "PCI66",
+               "Unknown", "Unknown",
+               "PCIX33", "PCIX66",
+               "PCIX100", "PCIX133"
+       };
+
+       seq_printf(m, "vendor\t\t: Radstone Technology\n");
+       seq_printf(m, "machine\t\t: PPC7D\n");
+
+       val = inb(PPC7D_CPLD_BOARD_REVISION);
+       val1 = (val & PPC7D_CPLD_BOARD_REVISION_NUMBER_MASK) >> 5;
+       val2 = (val & PPC7D_CPLD_BOARD_REVISION_LETTER_MASK);
+       seq_printf(m, "revision\t: %hd%c%c\n",
+                  val1,
+                  (val2 <= 0x18) ? 'A' + val2 : 'Y',
+                  (val2 > 0x18) ? 'A' + (val2 - 0x19) : ' ');
+
+       val = inb(PPC7D_CPLD_MOTHERBOARD_TYPE);
+       val1 = val & PPC7D_CPLD_MB_TYPE_PLL_MASK;
+       val2 = val & (PPC7D_CPLD_MB_TYPE_ECC_FITTED_MASK |
+                     PPC7D_CPLD_MB_TYPE_ECC_ENABLE_MASK);
+       seq_printf(m, "bus speed\t: %dMHz\n",
+                  (val1 == PPC7D_CPLD_MB_TYPE_PLL_133) ? 133 :
+                  (val1 == PPC7D_CPLD_MB_TYPE_PLL_100) ? 100 :
+                  (val1 == PPC7D_CPLD_MB_TYPE_PLL_64) ? 64 : 0);
+
+       val = inb(PPC7D_CPLD_MEM_CONFIG);
+       if (val & PPC7D_CPLD_SDRAM_BANK_NUM_MASK) sdram_num_banks--;
+
+       val = inb(PPC7D_CPLD_MEM_CONFIG_EXTEND);
+       val1 = (val & PPC7D_CPLD_SDRAM_BANK_SIZE_MASK) >> 6;
+       seq_printf(m, "SDRAM\t\t: %d banks of %d%c, total %d%c",
+                  sdram_num_banks,
+                  sdram_bank_sizes[val1],
+                  (sdram_bank_sizes[val1] < 128) ? 'G' : 'M',
+                  sdram_num_banks * sdram_bank_sizes[val1],
+                  (sdram_bank_sizes[val1] < 128) ? 'G' : 'M');
+       if (val2 & PPC7D_CPLD_MB_TYPE_ECC_FITTED_MASK) {
+               seq_printf(m, " [ECC %sabled]",
+                          (val2 & PPC7D_CPLD_MB_TYPE_ECC_ENABLE_MASK) ? "en" :
+                          "dis");
+       }
+       seq_printf(m, "\n");
+
+       val1 = (val & PPC7D_CPLD_FLASH_DEV_SIZE_MASK);
+       val2 = (val & PPC7D_CPLD_FLASH_BANK_NUM_MASK) >> 2;
+       seq_printf(m, "FLASH\t\t: %d banks of %dM, total %dM\n",
+                  flash_banks[val2], flash_sizes[val1],
+                  flash_banks[val2] * flash_sizes[val1]);
+
+       val = inb(PPC7D_CPLD_FLASH_WRITE_CNTL);
+       val1 = inb(PPC7D_CPLD_SW_FLASH_WRITE_PROTECT);
+       seq_printf(m, "  write links\t: %s%s%s%s\n",
+                  (val & PPD7D_CPLD_FLASH_CNTL_WR_LINK_MASK) ? "WRITE " : "",
+                  (val & PPD7D_CPLD_FLASH_CNTL_BOOT_LINK_MASK) ? "BOOT " : "",
+                  (val & PPD7D_CPLD_FLASH_CNTL_USER_LINK_MASK) ? "USER " : "",
+                  (val & (PPD7D_CPLD_FLASH_CNTL_WR_LINK_MASK |
+                          PPD7D_CPLD_FLASH_CNTL_BOOT_LINK_MASK |
+                          PPD7D_CPLD_FLASH_CNTL_USER_LINK_MASK)) ==
+                  0 ? "NONE" : "");
+       seq_printf(m, "  write sector h/w enables: %s%s%s%s%s\n",
+                  (val & PPD7D_CPLD_FLASH_CNTL_RECO_WR_MASK) ? "RECOVERY " :
+                  "",
+                  (val & PPD7D_CPLD_FLASH_CNTL_BOOT_WR_MASK) ? "BOOT " : "",
+                  (val & PPD7D_CPLD_FLASH_CNTL_USER_WR_MASK) ? "USER " : "",
+                  (val1 & PPC7D_CPLD_FLASH_CNTL_NVRAM_PROT_MASK) ? "NVRAM " :
+                  "",
+                  (((val &
+                     (PPD7D_CPLD_FLASH_CNTL_RECO_WR_MASK |
+                      PPD7D_CPLD_FLASH_CNTL_BOOT_WR_MASK |
+                      PPD7D_CPLD_FLASH_CNTL_BOOT_WR_MASK)) == 0)
+                   && ((val1 & PPC7D_CPLD_FLASH_CNTL_NVRAM_PROT_MASK) ==
+                       0)) ? "NONE" : "");
+       val1 =
+           inb(PPC7D_CPLD_SW_FLASH_WRITE_PROTECT) &
+           (PPC7D_CPLD_SW_FLASH_WRPROT_SYSBOOT_MASK |
+            PPC7D_CPLD_SW_FLASH_WRPROT_USER_MASK);
+       seq_printf(m, "  software sector enables: %s%s%s\n",
+                  (val1 & PPC7D_CPLD_SW_FLASH_WRPROT_SYSBOOT_MASK) ? "SYSBOOT "
+                  : "",
+                  (val1 & PPC7D_CPLD_SW_FLASH_WRPROT_USER_MASK) ? "USER " : "",
+                  (val1 == 0) ? "NONE " : "");
+
+       seq_printf(m, "Boot options\t: %s%s%s%s\n",
+                  (val & PPC7D_CPLD_FLASH_CNTL_ALTBOOT_LINK_MASK) ?
+                  "ALTERNATE " : "",
+                  (val & PPC7D_CPLD_FLASH_CNTL_VMEBOOT_LINK_MASK) ? "VME " :
+                  "",
+                  (val & PPC7D_CPLD_FLASH_CNTL_RECBOOT_LINK_MASK) ? "RECOVERY "
+                  : "",
+                  ((val &
+                    (PPC7D_CPLD_FLASH_CNTL_ALTBOOT_LINK_MASK |
+                     PPC7D_CPLD_FLASH_CNTL_VMEBOOT_LINK_MASK |
+                     PPC7D_CPLD_FLASH_CNTL_RECBOOT_LINK_MASK)) ==
+                   0) ? "NONE" : "");
+
+       val = inb(PPC7D_CPLD_EQUIPMENT_PRESENT_1);
+       seq_printf(m, "Fitted modules\t: %s%s%s%s\n",
+                  (val & PPC7D_CPLD_EQPT_PRES_1_PMC1_MASK) ? "" : "PMC1 ",
+                  (val & PPC7D_CPLD_EQPT_PRES_1_PMC2_MASK) ? "" : "PMC2 ",
+                  (val & PPC7D_CPLD_EQPT_PRES_1_AFIX_MASK) ? "AFIX " : "",
+                  ((val & (PPC7D_CPLD_EQPT_PRES_1_PMC1_MASK |
+                           PPC7D_CPLD_EQPT_PRES_1_PMC2_MASK |
+                           PPC7D_CPLD_EQPT_PRES_1_AFIX_MASK)) ==
+                   (PPC7D_CPLD_EQPT_PRES_1_PMC1_MASK |
+                    PPC7D_CPLD_EQPT_PRES_1_PMC2_MASK)) ? "NONE" : "");
+
+       if (val & PPC7D_CPLD_EQPT_PRES_1_AFIX_MASK) {
+               static const char *ids[] = {
+                       "unknown",
+                       "1553 (Dual Channel)",
+                       "1553 (Single Channel)",
+                       "8-bit SCSI + VGA",
+                       "16-bit SCSI + VGA",
+                       "1553 (Single Channel with sideband)",
+                       "1553 (Dual Channel with sideband)",
+                       NULL
+               };
+               u8 id = __raw_readb((void *)PPC7D_AFIX_REG_BASE + 0x03);
+               seq_printf(m, "AFIX module\t: 0x%hx [%s]\n", id,
+                          id < 7 ? ids[id] : "unknown");
+       }
+
+       val = inb(PPC7D_CPLD_PCI_CONFIG);
+       val1 = (val & PPC7D_CPLD_PCI_CONFIG_PCI0_MASK) >> 4;
+       val2 = (val & PPC7D_CPLD_PCI_CONFIG_PCI1_MASK);
+       seq_printf(m, "PCI#0\t\t: %s\nPCI#1\t\t: %s\n",
+                  pci_modes[val1], pci_modes[val2]);
+
+       val = inb(PPC7D_CPLD_EQUIPMENT_PRESENT_2);
+       seq_printf(m, "PMC1\t\t: %s\nPMC2\t\t: %s\n",
+                  (val & PPC7D_CPLD_EQPT_PRES_3_PMC1_V_MASK) ? "3.3v" : "5v",
+                  (val & PPC7D_CPLD_EQPT_PRES_3_PMC2_V_MASK) ? "3.3v" : "5v");
+       seq_printf(m, "PMC power source: %s\n",
+                  (val & PPC7D_CPLD_EQPT_PRES_3_PMC_POWER_MASK) ? "VME" :
+                  "internal");
+
+       val = inb(PPC7D_CPLD_EQUIPMENT_PRESENT_4);
+       val2 = inb(PPC7D_CPLD_EQUIPMENT_PRESENT_2);
+       seq_printf(m, "Fit options\t: %s%s%s%s%s%s%s\n",
+                  (val & PPC7D_CPLD_EQPT_PRES_4_LPT_MASK) ? "LPT " : "",
+                  (val & PPC7D_CPLD_EQPT_PRES_4_PS2_FITTED) ? "PS2 " : "",
+                  (val & PPC7D_CPLD_EQPT_PRES_4_USB2_FITTED) ? "USB2 " : "",
+                  (val2 & PPC7D_CPLD_EQPT_PRES_2_UNIVERSE_MASK) ? "VME " : "",
+                  (val2 & PPC7D_CPLD_EQPT_PRES_2_COM36_MASK) ? "COM3-6 " : "",
+                  (val2 & PPC7D_CPLD_EQPT_PRES_2_GIGE_MASK) ? "eth0 " : "",
+                  (val2 & PPC7D_CPLD_EQPT_PRES_2_DUALGIGE_MASK) ? "eth1 " :
+                  "");
+
+       val = inb(PPC7D_CPLD_ID_LINK);
+       val1 = val & (PPC7D_CPLD_ID_LINK_E6_MASK |
+                     PPC7D_CPLD_ID_LINK_E7_MASK |
+                     PPC7D_CPLD_ID_LINK_E12_MASK |
+                     PPC7D_CPLD_ID_LINK_E13_MASK);
+
+       val = inb(PPC7D_CPLD_FLASH_WRITE_CNTL) &
+           (PPD7D_CPLD_FLASH_CNTL_WR_LINK_MASK |
+            PPD7D_CPLD_FLASH_CNTL_BOOT_LINK_MASK |
+            PPD7D_CPLD_FLASH_CNTL_USER_LINK_MASK);
+
+       seq_printf(m, "Board links present: %s%s%s%s%s%s%s%s\n",
+                  (val1 & PPC7D_CPLD_ID_LINK_E6_MASK) ? "E6 " : "",
+                  (val1 & PPC7D_CPLD_ID_LINK_E7_MASK) ? "E7 " : "",
+                  (val & PPD7D_CPLD_FLASH_CNTL_WR_LINK_MASK) ? "E9 " : "",
+                  (val & PPD7D_CPLD_FLASH_CNTL_BOOT_LINK_MASK) ? "E10 " : "",
+                  (val & PPD7D_CPLD_FLASH_CNTL_USER_LINK_MASK) ? "E11 " : "",
+                  (val1 & PPC7D_CPLD_ID_LINK_E12_MASK) ? "E12 " : "",
+                  (val1 & PPC7D_CPLD_ID_LINK_E13_MASK) ? "E13 " : "",
+                  ((val == 0) && (val1 == 0)) ? "NONE" : "");
+
+       val = inb(PPC7D_CPLD_WDOG_RESETSW_MASK);
+       seq_printf(m, "Front panel reset switch: %sabled\n",
+                  (val & PPC7D_CPLD_WDOG_RESETSW_MASK) ? "dis" : "en");
+
+       return 0;
+}
+
+static void __init ppc7d_calibrate_decr(void)
+{
+       ulong freq;
+
+       freq = 100000000 / 4;
+
+       pr_debug("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);
+}
+
+/*****************************************************************************
+ * Interrupt stuff
+ *****************************************************************************/
+
+static irqreturn_t ppc7d_i8259_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+       u32 temp = mv64x60_read(&bh, MV64x60_GPP_INTR_CAUSE);
+       if (temp & (1 << 28)) {
+               i8259_irq(regs);
+               mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, temp & (~(1 << 28)));
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+}
+
+/*
+ * Each interrupt cause is assigned an IRQ number.
+ * Southbridge has 16*2 (two 8259's) interrupts.
+ * Discovery-II has 96 interrupts (cause-hi, cause-lo, gpp x 32).
+ * If multiple interrupts are pending, get_irq() returns the
+ * lowest pending irq number first.
+ *
+ *
+ * IRQ #   Source                              Trig   Active
+ * =============================================================
+ *
+ * Southbridge
+ * -----------
+ * IRQ #   Source                              Trig
+ * =============================================================
+ * 0       ISA High Resolution Counter         Edge
+ * 1       Keyboard                            Edge
+ * 2       Cascade From (IRQ 8-15)             Edge
+ * 3       Com 2 (Uart 2)                      Edge
+ * 4       Com 1 (Uart 1)                      Edge
+ * 5       PCI Int D/AFIX IRQZ ID4 (2,7)       Level
+ * 6       GPIO                                Level
+ * 7       LPT                                 Edge
+ * 8       RTC Alarm                           Edge
+ * 9       PCI Int A/PMC 2/AFIX IRQW ID1 (2,0) Level
+ * 10      PCI Int B/PMC 1/AFIX IRQX ID2 (2,1) Level
+ * 11      USB2                                Level
+ * 12      Mouse                               Edge
+ * 13      Reserved internally by Ali M1535+
+ * 14      PCI Int C/VME/AFIX IRQY ID3 (2,6)   Level
+ * 15      COM 5/6                             Level
+ *
+ * 16..112 Discovery-II...
+ *
+ * MPP28   Southbridge                         Edge   High
+ *
+ *
+ * Interrupts are cascaded through to the Discovery-II.
+ *
+ *  PCI ---
+ *         \
+ * CPLD --> ALI1535 -------> DISCOVERY-II
+ *        INTF           MPP28
+ */
+static void __init ppc7d_init_irq(void)
+{
+       int irq;
+
+       pr_debug("%s\n", __FUNCTION__);
+       i8259_init(0);
+       mv64360_init_irq();
+
+       /* IRQ 0..15 are handled by the cascaded 8259's of the Ali1535 */
+       for (irq = 0; irq < 16; irq++) {
+               irq_desc[irq].handler = &i8259_pic;
+       }
+       /* IRQs 5,6,9,10,11,14,15 are level sensitive */
+       irq_desc[5].status |= IRQ_LEVEL;
+       irq_desc[6].status |= IRQ_LEVEL;
+       irq_desc[9].status |= IRQ_LEVEL;
+       irq_desc[10].status |= IRQ_LEVEL;
+       irq_desc[11].status |= IRQ_LEVEL;
+       irq_desc[14].status |= IRQ_LEVEL;
+       irq_desc[15].status |= IRQ_LEVEL;
+
+       /* GPP28 is edge triggered */
+       irq_desc[mv64360_irq_base + MV64x60_IRQ_GPP28].status &= ~IRQ_LEVEL;
+}
+
+static u32 ppc7d_irq_canonicalize(u32 irq)
+{
+       if ((irq >= 16) && (irq < (16 + 96)))
+               irq -= 16;
+
+       return irq;
+}
+
+static int ppc7d_get_irq(struct pt_regs *regs)
+{
+       int irq;
+
+       irq = mv64360_get_irq(regs);
+       if (irq == (mv64360_irq_base + MV64x60_IRQ_GPP28))
+               irq = i8259_irq(regs);
+       return irq;
+}
+
+/*
+ * 9       PCI Int A/PMC 2/AFIX IRQW ID1 (2,0) Level
+ * 10      PCI Int B/PMC 1/AFIX IRQX ID2 (2,1) Level
+ * 14      PCI Int C/VME/AFIX IRQY ID3 (2,6)   Level
+ * 5       PCI Int D/AFIX IRQZ ID4 (2,7)       Level
+ */
+static int __init ppc7d_map_irq(struct pci_dev *dev, unsigned char idsel,
+                               unsigned char pin)
+{
+       static const char pci_irq_table[][4] =
+           /*
+            *      PCI IDSEL/INTPIN->INTLINE
+            *         A   B   C   D
+            */
+       {
+               {10, 14, 5, 9}, /* IDSEL 10 - PMC2 / AFIX IRQW */
+               {9, 10, 14, 5}, /* IDSEL 11 - PMC1 / AFIX IRQX */
+               {5, 9, 10, 14}, /* IDSEL 12 - AFIX IRQY */
+               {14, 5, 9, 10}, /* IDSEL 13 - AFIX IRQZ */
+       };
+       const long min_idsel = 10, max_idsel = 14, irqs_per_slot = 4;
+
+       pr_debug("%s: %04x/%04x/%x: idsel=%hx pin=%hu\n", __FUNCTION__,
+                dev->vendor, dev->device, PCI_FUNC(dev->devfn), idsel, pin);
+
+       return PCI_IRQ_TABLE_LOOKUP;
+}
+
+void __init ppc7d_intr_setup(void)
+{
+       u32 data;
+
+       /*
+        * Define GPP 28 interrupt polarity as active high
+        * input signal and level triggered
+        */
+       data = mv64x60_read(&bh, MV64x60_GPP_LEVEL_CNTL);
+       data &= ~(1 << 28);
+       mv64x60_write(&bh, MV64x60_GPP_LEVEL_CNTL, data);
+       data = mv64x60_read(&bh, MV64x60_GPP_IO_CNTL);
+       data &= ~(1 << 28);
+       mv64x60_write(&bh, MV64x60_GPP_IO_CNTL, data);
+
+       /* Config GPP intr ctlr to respond to level trigger */
+       data = mv64x60_read(&bh, MV64x60_COMM_ARBITER_CNTL);
+       data |= (1 << 10);
+       mv64x60_write(&bh, MV64x60_COMM_ARBITER_CNTL, data);
+
+       /* XXXX Erranum FEr PCI-#8 */
+       data = mv64x60_read(&bh, MV64x60_PCI0_CMD);
+       data &= ~((1 << 5) | (1 << 9));
+       mv64x60_write(&bh, MV64x60_PCI0_CMD, data);
+       data = mv64x60_read(&bh, MV64x60_PCI1_CMD);
+       data &= ~((1 << 5) | (1 << 9));
+       mv64x60_write(&bh, MV64x60_PCI1_CMD, data);
+
+       /*
+        * Dismiss and then enable interrupt on GPP interrupt cause
+        * for CPU #0
+        */
+       mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, ~(1 << 28));
+       data = mv64x60_read(&bh, MV64x60_GPP_INTR_MASK);
+       data |= (1 << 28);
+       mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, data);
+
+       /*
+        * Dismiss and then enable interrupt on CPU #0 high cause reg
+        * BIT27 summarizes GPP interrupts 23-31
+        */
+       mv64x60_write(&bh, MV64360_IC_MAIN_CAUSE_HI, ~(1 << 27));
+       data = mv64x60_read(&bh, MV64360_IC_CPU0_INTR_MASK_HI);
+       data |= (1 << 27);
+       mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI, data);
+}
+
+/*****************************************************************************
+ * Platform device data fixup routines.
+ *****************************************************************************/
+
+#if defined(CONFIG_SERIAL_MPSC)
+static void __init ppc7d_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 = PPC7D_DEFAULT_BAUD;
+       pdata->brg_clk_src = PPC7D_MPSC_CLK_SRC;
+       pdata->brg_clk_freq = PPC7D_MPSC_CLK_FREQ;
+
+       return;
+}
+#endif
+
+#if defined(CONFIG_MV643XX_ETH)
+static void __init ppc7d_fixup_eth_pdata(struct platform_device *pdev)
+{
+       struct mv643xx_eth_platform_data *eth_pd;
+       static u16 phy_addr[] = {
+               PPC7D_ETH0_PHY_ADDR,
+               PPC7D_ETH1_PHY_ADDR,
+               PPC7D_ETH2_PHY_ADDR,
+       };
+       int i;
+
+       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 = PPC7D_ETH_TX_QUEUE_SIZE;
+       eth_pd->rx_queue_size = PPC7D_ETH_RX_QUEUE_SIZE;
+
+       /* Adjust IRQ by mv64360_irq_base */
+       for (i = 0; i < pdev->num_resources; i++) {
+               struct resource *r = &pdev->resource[i];
+
+               if (r->flags & IORESOURCE_IRQ) {
+                       r->start += mv64360_irq_base;
+                       r->end += mv64360_irq_base;
+                       pr_debug("%s, uses IRQ %d\n", pdev->name,
+                                (int)r->start);
+               }
+       }
+
+}
+#endif
+
+#if defined(CONFIG_I2C_MV64XXX)
+static void __init
+ppc7d_fixup_i2c_pdata(struct platform_device *pdev)
+{
+       struct mv64xxx_i2c_pdata *pdata;
+       int i;
+
+       pdata = pdev->dev.platform_data;
+       if (pdata == NULL) {
+               pdata = kmalloc(sizeof(*pdata), GFP_KERNEL);
+               if (pdata == NULL)
+                       return;
+
+               memset(pdata, 0, sizeof(*pdata));
+               pdev->dev.platform_data = pdata;
+       }
+
+       /* divisors M=8, N=3 for 100kHz I2C from 133MHz system clock */
+       pdata->freq_m = 8;
+       pdata->freq_n = 3;
+       pdata->timeout = 500;
+       pdata->retries = 3;
+
+       /* Adjust IRQ by mv64360_irq_base */
+       for (i = 0; i < pdev->num_resources; i++) {
+               struct resource *r = &pdev->resource[i];
+
+               if (r->flags & IORESOURCE_IRQ) {
+                       r->start += mv64360_irq_base;
+                       r->end += mv64360_irq_base;
+                       pr_debug("%s, uses IRQ %d\n", pdev->name, (int) r->start);
+               }
+       }
+}
+#endif
+
+static int __init ppc7d_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", ppc7d_fixup_mpsc_pdata },
+               { MPSC_CTLR_NAME ".1", ppc7d_fixup_mpsc_pdata },
+#endif
+#if defined(CONFIG_MV643XX_ETH)
+               { MV643XX_ETH_NAME ".0", ppc7d_fixup_eth_pdata },
+               { MV643XX_ETH_NAME ".1", ppc7d_fixup_eth_pdata },
+               { MV643XX_ETH_NAME ".2", ppc7d_fixup_eth_pdata },
+#endif
+#if defined(CONFIG_I2C_MV64XXX)
+               { MV64XXX_I2C_CTLR_NAME ".0", ppc7d_fixup_i2c_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;
+}
+
+/*****************************************************************************
+ * PCI device fixups.
+ * These aren't really fixups per se. They are used to init devices as they
+ * are found during PCI scan.
+ *
+ * The PPC7D has an HB8 PCI-X bridge which must be set up during a PCI
+ * scan in order to find other devices on its secondary side.
+ *****************************************************************************/
+
+static void __init ppc7d_fixup_hb8(struct pci_dev *dev)
+{
+       u16 val16;
+
+       if (dev->bus->number == 0) {
+               pr_debug("PCI: HB8 init\n");
+
+               pci_write_config_byte(dev, 0x1c,
+                                     ((PPC7D_PCI0_IO_START_PCI_ADDR & 0xf000)
+                                      >> 8) | 0x01);
+               pci_write_config_byte(dev, 0x1d,
+                                     (((PPC7D_PCI0_IO_START_PCI_ADDR +
+                                        PPC7D_PCI0_IO_SIZE -
+                                        1) & 0xf000) >> 8) | 0x01);
+               pci_write_config_word(dev, 0x30,
+                                     PPC7D_PCI0_IO_START_PCI_ADDR >> 16);
+               pci_write_config_word(dev, 0x32,
+                                     ((PPC7D_PCI0_IO_START_PCI_ADDR +
+                                       PPC7D_PCI0_IO_SIZE -
+                                       1) >> 16) & 0xffff);
+
+               pci_write_config_word(dev, 0x20,
+                                     PPC7D_PCI0_MEM0_START_PCI_LO_ADDR >> 16);
+               pci_write_config_word(dev, 0x22,
+                                     ((PPC7D_PCI0_MEM0_START_PCI_LO_ADDR +
+                                       PPC7D_PCI0_MEM0_SIZE -
+                                       1) >> 16) & 0xffff);
+               pci_write_config_word(dev, 0x24, 0);
+               pci_write_config_word(dev, 0x26, 0);
+               pci_write_config_dword(dev, 0x28, 0);
+               pci_write_config_dword(dev, 0x2c, 0);
+
+               pci_read_config_word(dev, 0x3e, &val16);
+               val16 |= ((1 << 5) | (1 << 1)); /* signal master aborts and
+                                                * SERR to primary
+                                                */
+               val16 &= ~(1 << 2);             /* ISA disable, so all ISA
+                                                * ports forwarded to secondary
+                                                */
+               pci_write_config_word(dev, 0x3e, val16);
+       }
+}
+
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_HINT, 0x0028, ppc7d_fixup_hb8);
+
+/* This should perhaps be a separate driver as we're actually initializing
+ * the chip for this board here. It's hardly a fixup...
+ */
+static void __init ppc7d_fixup_ali1535(struct pci_dev *dev)
+{
+       pr_debug("PCI: ALI1535 init\n");
+
+       if (dev->bus->number == 1) {
+               /* Configure the ISA Port Settings */
+               pci_write_config_byte(dev, 0x43, 0x00);
+
+               /* Disable PCI Interrupt polling mode */
+               pci_write_config_byte(dev, 0x45, 0x00);
+
+               /* Multifunction pin select INTFJ -> INTF */
+               pci_write_config_byte(dev, 0x78, 0x00);
+
+               /* Set PCI INT -> IRQ Routing control in for external
+                * pins south bridge.
+                */
+               pci_write_config_byte(dev, 0x48, 0x31); /* [7-4] INT B -> IRQ10
+                                                        * [3-0] INT A -> IRQ9
+                                                        */
+               pci_write_config_byte(dev, 0x49, 0x5D); /* [7-4] INT D -> IRQ5
+                                                        * [3-0] INT C -> IRQ14
+                                                        */
+
+               /* PPC7D setup */
+               /* NEC USB device on IRQ 11 (INTE) - INTF disabled */
+               pci_write_config_byte(dev, 0x4A, 0x09);
+
+               /* GPIO on IRQ 6 */
+               pci_write_config_byte(dev, 0x76, 0x07);
+
+               /* SIRQ I (COMS 5/6) use IRQ line 15.
+                * Positive (not subtractive) address decode.
+                */
+               pci_write_config_byte(dev, 0x44, 0x0f);
+
+               /* SIRQ II disabled */
+               pci_write_config_byte(dev, 0x75, 0x0);
+
+               /* On board USB and RTC disabled */
+               pci_write_config_word(dev, 0x52, (1 << 14));
+               pci_write_config_byte(dev, 0x74, 0x00);
+
+               /* On board IDE disabled */
+               pci_write_config_byte(dev, 0x58, 0x00);
+
+               /* Decode 32-bit addresses */
+               pci_write_config_byte(dev, 0x5b, 0);
+
+               /* Disable docking IO */
+               pci_write_config_word(dev, 0x5c, 0x0000);
+
+               /* Disable modem, enable sound */
+               pci_write_config_byte(dev, 0x77, (1 << 6));
+
+               /* Disable hot-docking mode */
+               pci_write_config_byte(dev, 0x7d, 0x00);
+       }
+}
+
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, 0x1533, ppc7d_fixup_ali1535);
+
+static int ppc7d_pci_exclude_device(u8 bus, u8 devfn)
+{
+       /* Early versions of this board were fitted with IBM ALMA
+        * PCI-VME bridge chips. The PCI config space of these devices
+        * was not set up correctly and causes PCI scan problems.
+        */
+       if ((bus == 1) && (PCI_SLOT(devfn) == 4) && ppc7d_has_alma)
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       return mv64x60_pci_exclude_device(bus, devfn);
+}
+
+/* This hook is called when each PCI bus is probed.
+ */
+static void ppc7d_pci_fixup_bus(struct pci_bus *bus)
+{
+       pr_debug("PCI BUS %hu: %lx/%lx %lx/%lx %lx/%lx %lx/%lx\n",
+                bus->number,
+                bus->resource[0] ? bus->resource[0]->start : 0,
+                bus->resource[0] ? bus->resource[0]->end : 0,
+                bus->resource[1] ? bus->resource[1]->start : 0,
+                bus->resource[1] ? bus->resource[1]->end : 0,
+                bus->resource[2] ? bus->resource[2]->start : 0,
+                bus->resource[2] ? bus->resource[2]->end : 0,
+                bus->resource[3] ? bus->resource[3]->start : 0,
+                bus->resource[3] ? bus->resource[3]->end : 0);
+
+       if ((bus->number == 1) && (bus->resource[2] != NULL)) {
+               /* Hide PCI window 2 of Bus 1 which is used only to
+                * map legacy ISA memory space.
+                */
+               bus->resource[2]->start = 0;
+               bus->resource[2]->end = 0;
+               bus->resource[2]->flags = 0;
+       }
+}
+
+/*****************************************************************************
+ * Board device setup code
+ *****************************************************************************/
+
+void __init ppc7d_setup_peripherals(void)
+{
+       u32 val32;
+
+       /* Set up windows for boot CS */
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2BOOT_WIN,
+                                PPC7D_BOOT_WINDOW_BASE, PPC7D_BOOT_WINDOW_SIZE,
+                                0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2BOOT_WIN);
+
+       /* Boot firmware configures the following DevCS addresses.
+        * DevCS0 - board control/status
+        * DevCS1 - test registers
+        * DevCS2 - AFIX port/address registers (for identifying)
+        * DevCS3 - FLASH
+        *
+        * We don't use DevCS0, DevCS1.
+        */
+       val32 = mv64x60_read(&bh, MV64360_CPU_BAR_ENABLE);
+       val32 |= ((1 << 4) | (1 << 5));
+       mv64x60_write(&bh, MV64360_CPU_BAR_ENABLE, val32);
+       mv64x60_write(&bh, MV64x60_CPU2DEV_0_BASE, 0);
+       mv64x60_write(&bh, MV64x60_CPU2DEV_0_SIZE, 0);
+       mv64x60_write(&bh, MV64x60_CPU2DEV_1_BASE, 0);
+       mv64x60_write(&bh, MV64x60_CPU2DEV_1_SIZE, 0);
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_2_WIN,
+                                PPC7D_AFIX_REG_BASE, PPC7D_AFIX_REG_SIZE, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_2_WIN);
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2DEV_3_WIN,
+                                PPC7D_FLASH_BASE, PPC7D_FLASH_SIZE_ACTUAL, 0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2DEV_3_WIN);
+
+       mv64x60_set_32bit_window(&bh, MV64x60_CPU2SRAM_WIN,
+                                PPC7D_INTERNAL_SRAM_BASE, MV64360_SRAM_SIZE,
+                                0);
+       bh.ci->enable_window_32bit(&bh, MV64x60_CPU2SRAM_WIN);
+
+       /* Set up Enet->SRAM window */
+       mv64x60_set_32bit_window(&bh, MV64x60_ENET2MEM_4_WIN,
+                                PPC7D_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 */
+       val32 = mv64x60_read(&bh, MV64360_ENET2MEM_ACC_PROT_0);
+       val32 |= (0x3 << (4 << 1));
+       mv64x60_write(&bh, MV64360_ENET2MEM_ACC_PROT_0, val32);
+       val32 = mv64x60_read(&bh, MV64360_ENET2MEM_ACC_PROT_1);
+       val32 |= (0x3 << (4 << 1));
+       mv64x60_write(&bh, MV64360_ENET2MEM_ACC_PROT_1, val32);
+       val32 = mv64x60_read(&bh, MV64360_ENET2MEM_ACC_PROT_2);
+       val32 |= (0x3 << (4 << 1));
+       mv64x60_write(&bh, MV64360_ENET2MEM_ACC_PROT_2, val32);
+
+       val32 = mv64x60_read(&bh, MV64x60_TIMR_CNTR_0_3_CNTL);
+       val32 &= ~((1 << 0) | (1 << 8) | (1 << 16) | (1 << 24));
+       mv64x60_write(&bh, MV64x60_TIMR_CNTR_0_3_CNTL, val32);
+
+       /* Enumerate pci bus.
+        *
+        * We scan PCI#0 first (the bus with the HB8 and other
+        * on-board peripherals). We must configure the 64360 before
+        * each scan, according to the bus number assignments.  Busses
+        * are assigned incrementally, starting at 0.  PCI#0 is
+        * usually assigned bus#0, the secondary side of the HB8 gets
+        * bus#1 and PCI#1 (second PMC site) gets bus#2.  However, if
+        * any PMC card has a PCI bridge, these bus assignments will
+        * change.
+        */
+
+       /* Turn off PCI retries */
+       val32 = mv64x60_read(&bh, MV64x60_CPU_CONFIG);
+       val32 |= (1 << 17);
+       mv64x60_write(&bh, MV64x60_CPU_CONFIG, val32);
+
+       /* Scan PCI#0 */
+       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);
+       printk(KERN_INFO "PCI#0: first=%d last=%d\n",
+              bh.hose_a->first_busno, bh.hose_a->last_busno);
+
+       /* Scan PCI#1 */
+       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);
+       printk(KERN_INFO "PCI#1: first=%d last=%d\n",
+              bh.hose_b->first_busno, bh.hose_b->last_busno);
+
+       /* Turn on PCI retries */
+       val32 = mv64x60_read(&bh, MV64x60_CPU_CONFIG);
+       val32 &= ~(1 << 17);
+       mv64x60_write(&bh, MV64x60_CPU_CONFIG, val32);
+
+       /* Setup interrupts */
+       ppc7d_intr_setup();
+}
+
+static void __init ppc7d_setup_bridge(void)
+{
+       struct mv64x60_setup_info si;
+       int i;
+       u32 temp;
+
+       mv64360_irq_base = 16;  /* first 16 intrs are 2 x 8259's */
+
+       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 = PPC7D_PCI0_IO_START_PROC_ADDR;
+       si.pci_0.pci_io.pci_base_hi = 0;
+       si.pci_0.pci_io.pci_base_lo = PPC7D_PCI0_IO_START_PCI_ADDR;
+       si.pci_0.pci_io.size = PPC7D_PCI0_IO_SIZE;
+       si.pci_0.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE;
+       si.pci_0.pci_mem[0].cpu_base = PPC7D_PCI0_MEM0_START_PROC_ADDR;
+       si.pci_0.pci_mem[0].pci_base_hi = PPC7D_PCI0_MEM0_START_PCI_HI_ADDR;
+       si.pci_0.pci_mem[0].pci_base_lo = PPC7D_PCI0_MEM0_START_PCI_LO_ADDR;
+       si.pci_0.pci_mem[0].size = PPC7D_PCI0_MEM0_SIZE;
+       si.pci_0.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE;
+       si.pci_0.pci_mem[1].cpu_base = PPC7D_PCI0_MEM1_START_PROC_ADDR;
+       si.pci_0.pci_mem[1].pci_base_hi = PPC7D_PCI0_MEM1_START_PCI_HI_ADDR;
+       si.pci_0.pci_mem[1].pci_base_lo = PPC7D_PCI0_MEM1_START_PCI_LO_ADDR;
+       si.pci_0.pci_mem[1].size = PPC7D_PCI0_MEM1_SIZE;
+       si.pci_0.pci_mem[1].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 = PPC7D_PCI1_IO_START_PROC_ADDR;
+       si.pci_1.pci_io.pci_base_hi = 0;
+       si.pci_1.pci_io.pci_base_lo = PPC7D_PCI1_IO_START_PCI_ADDR;
+       si.pci_1.pci_io.size = PPC7D_PCI1_IO_SIZE;
+       si.pci_1.pci_io.swap = MV64x60_CPU2PCI_SWAP_NONE;
+       si.pci_1.pci_mem[0].cpu_base = PPC7D_PCI1_MEM0_START_PROC_ADDR;
+       si.pci_1.pci_mem[0].pci_base_hi = PPC7D_PCI1_MEM0_START_PCI_HI_ADDR;
+       si.pci_1.pci_mem[0].pci_base_lo = PPC7D_PCI1_MEM0_START_PCI_LO_ADDR;
+       si.pci_1.pci_mem[0].size = PPC7D_PCI1_MEM0_SIZE;
+       si.pci_1.pci_mem[0].swap = MV64x60_CPU2PCI_SWAP_NONE;
+       si.pci_1.pci_mem[1].cpu_base = PPC7D_PCI1_MEM1_START_PROC_ADDR;
+       si.pci_1.pci_mem[1].pci_base_hi = PPC7D_PCI1_MEM1_START_PCI_HI_ADDR;
+       si.pci_1.pci_mem[1].pci_base_lo = PPC7D_PCI1_MEM1_START_PCI_LO_ADDR;
+       si.pci_1.pci_mem[1].size = PPC7D_PCI1_MEM1_SIZE;
+       si.pci_1.pci_mem[1].swap = MV64x60_CPU2PCI_SWAP_NONE;
+       si.pci_1.pci_cmd_bits = 0;
+       si.pci_1.latency_timer = 0x80;
+
+       /* Don't clear the SRAM window since we use it for debug */
+       si.window_preserve_mask_32_lo = (1 << MV64x60_CPU2SRAM_WIN);
+
+       printk(KERN_INFO "PCI: MV64360 PCI#0 IO at %x, size %x\n",
+              si.pci_0.pci_io.cpu_base, si.pci_0.pci_io.size);
+       printk(KERN_INFO "PCI: MV64360 PCI#1 IO at %x, size %x\n",
+              si.pci_1.pci_io.cpu_base, si.pci_1.pci_io.size);
+
+       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_0.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;
+
+               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;
+               /* All PPC7D hardware uses B0 or newer MV64360 silicon which
+                * does not have snoop bugs.
+                */
+               si.enet_options[i] = MV64360_ENET2MEM_SNOOP_WB;
+               si.mpsc_options[i] = MV64360_MPSC2MEM_SNOOP_WB;
+               si.idma_options[i] = MV64360_IDMA2MEM_SNOOP_WB;
+
+               si.pci_0.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;
+
+               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_ERR "MV64360 initialization failed.\n");
+
+       pr_debug("MV64360 regs @ %lx/%p\n", bh.p_base, bh.v_base);
+
+       /* Enable WB Cache coherency on SRAM */
+       temp = mv64x60_read(&bh, MV64360_SRAM_CONFIG);
+       pr_debug("SRAM_CONFIG: %x\n", temp);
+#if defined(CONFIG_NOT_COHERENT_CACHE)
+       mv64x60_write(&bh, MV64360_SRAM_CONFIG, temp & ~0x2);
+#else
+       mv64x60_write(&bh, MV64360_SRAM_CONFIG, temp | 0x2);
+#endif
+       /* If system operates with internal bus arbiter (CPU master
+        * control bit8) clear AACK Delay bit [25] in CPU
+        * configuration register.
+        */
+       temp = mv64x60_read(&bh, MV64x60_CPU_MASTER_CNTL);
+       if (temp & (1 << 8)) {
+               temp = mv64x60_read(&bh, MV64x60_CPU_CONFIG);
+               mv64x60_write(&bh, MV64x60_CPU_CONFIG, (temp & ~(1 << 25)));
+       }
+
+       /* Data and address parity is enabled */
+       temp = mv64x60_read(&bh, MV64x60_CPU_CONFIG);
+       mv64x60_write(&bh, MV64x60_CPU_CONFIG,
+                     (temp | (1 << 26) | (1 << 19)));
+
+       pci_dram_offset = 0;    /* sys mem at same addr on PCI & cpu bus */
+       ppc_md.pci_swizzle = common_swizzle;
+       ppc_md.pci_map_irq = ppc7d_map_irq;
+       ppc_md.pci_exclude_device = ppc7d_pci_exclude_device;
+
+       mv64x60_set_bus(&bh, 0, 0);
+       bh.hose_a->first_busno = 0;
+       bh.hose_a->last_busno = 0xff;
+       bh.hose_a->mem_space.start = PPC7D_PCI0_MEM0_START_PCI_LO_ADDR;
+       bh.hose_a->mem_space.end =
+           PPC7D_PCI0_MEM0_START_PCI_LO_ADDR + PPC7D_PCI0_MEM0_SIZE;
+
+       /* These will be set later, as a result of PCI0 scan */
+       bh.hose_b->first_busno = 0;
+       bh.hose_b->last_busno = 0xff;
+       bh.hose_b->mem_space.start = PPC7D_PCI1_MEM0_START_PCI_LO_ADDR;
+       bh.hose_b->mem_space.end =
+           PPC7D_PCI1_MEM0_START_PCI_LO_ADDR + PPC7D_PCI1_MEM0_SIZE;
+
+       pr_debug("MV64360: PCI#0 IO decode %08x/%08x IO remap %08x\n",
+                mv64x60_read(&bh, 0x48), mv64x60_read(&bh, 0x50),
+                mv64x60_read(&bh, 0xf0));
+}
+
+static void __init ppc7d_setup_arch(void)
+{
+       int port;
+
+       loops_per_jiffy = 100000000 / HZ;
+
+#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
+
+       if ((cur_cpu_spec[0]->cpu_features & CPU_FTR_SPEC7450) ||
+           (cur_cpu_spec[0]->cpu_features & CPU_FTR_L3CR))
+               /* 745x is different.  We only want to pass along enable. */
+               _set_L2CR(L2CR_L2E);
+       else if (cur_cpu_spec[0]->cpu_features & CPU_FTR_L2CR)
+               /* All modules have 1MB of L2.  We also assume that an
+                * L2 divisor of 3 will work.
+                */
+               _set_L2CR(L2CR_L2E | L2CR_L2SIZ_1MB | L2CR_L2CLK_DIV3
+                         | L2CR_L2RAM_PIPE | L2CR_L2OH_1_0 | L2CR_L2DF);
+
+       if (cur_cpu_spec[0]->cpu_features & CPU_FTR_L3CR)
+               /* No L3 cache */
+               _set_L3CR(0);
+
+#ifdef CONFIG_DUMMY_CONSOLE
+       conswitchp = &dummy_con;
+#endif
+
+       /* Lookup PCI host bridges */
+       if (ppc_md.progress)
+               ppc_md.progress("ppc7d_setup_arch: calling setup_bridge", 0);
+
+       ppc7d_setup_bridge();
+       ppc7d_setup_peripherals();
+
+       /* Disable ethernet. It might have been setup by the bootrom */
+       for (port = 0; port < 3; port++)
+               mv64x60_write(&bh, MV643XX_ETH_RECEIVE_QUEUE_COMMAND_REG(port),
+                             0x0000ff00);
+
+       /* Clear queue pointers to ensure they are all initialized,
+        * otherwise since queues 1-7 are unused, they have random
+        * pointers which look strange in register dumps. Don't bother
+        * with queue 0 since it will be initialized later.
+        */
+       for (port = 0; port < 3; port++) {
+               mv64x60_write(&bh,
+                             MV643XX_ETH_RX_CURRENT_QUEUE_DESC_PTR_1(port),
+                             0x00000000);
+               mv64x60_write(&bh,
+                             MV643XX_ETH_RX_CURRENT_QUEUE_DESC_PTR_2(port),
+                             0x00000000);
+               mv64x60_write(&bh,
+                             MV643XX_ETH_RX_CURRENT_QUEUE_DESC_PTR_3(port),
+                             0x00000000);
+               mv64x60_write(&bh,
+                             MV643XX_ETH_RX_CURRENT_QUEUE_DESC_PTR_4(port),
+                             0x00000000);
+               mv64x60_write(&bh,
+                             MV643XX_ETH_RX_CURRENT_QUEUE_DESC_PTR_5(port),
+                             0x00000000);
+               mv64x60_write(&bh,
+                             MV643XX_ETH_RX_CURRENT_QUEUE_DESC_PTR_6(port),
+                             0x00000000);
+               mv64x60_write(&bh,
+                             MV643XX_ETH_RX_CURRENT_QUEUE_DESC_PTR_7(port),
+                             0x00000000);
+       }
+
+       printk(KERN_INFO "Radstone Technology PPC7D\n");
+       if (ppc_md.progress)
+               ppc_md.progress("ppc7d_setup_arch: exit", 0);
+
+}
+
+/* Real Time Clock support.
+ * PPC7D has a DS1337 accessed by I2C.
+ */
+static ulong ppc7d_get_rtc_time(void)
+{
+        struct rtc_time tm;
+        int result;
+
+        spin_lock(&rtc_lock);
+        result = ds1337_do_command(0, DS1337_GET_DATE, &tm);
+        spin_unlock(&rtc_lock);
+
+        if (result == 0)
+                result = mktime(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+        return result;
+}
+
+static int ppc7d_set_rtc_time(unsigned long nowtime)
+{
+        struct rtc_time tm;
+        int result;
+
+        spin_lock(&rtc_lock);
+        to_tm(nowtime, &tm);
+        result = ds1337_do_command(0, DS1337_SET_DATE, &tm);
+        spin_unlock(&rtc_lock);
+
+        return result;
+}
+
+/* This kernel command line parameter can be used to have the target
+ * wait for a JTAG debugger to attach. Of course, a JTAG debugger
+ * with hardware breakpoint support can have the target stop at any
+ * location during init, but this is a convenience feature that makes
+ * it easier in the common case of loading the code using the ppcboot
+ * bootloader..
+ */
+static unsigned long ppc7d_wait_debugger;
+
+static int __init ppc7d_waitdbg(char *str)
+{
+       ppc7d_wait_debugger = 1;
+       return 1;
+}
+
+__setup("waitdbg", ppc7d_waitdbg);
+
+/* Second phase board init, called after other (architecture common)
+ * low-level services have been initialized.
+ */
+static void ppc7d_init2(void)
+{
+       unsigned long flags;
+       u32 data;
+       u8 data8;
+
+       pr_debug("%s: enter\n", __FUNCTION__);
+
+       /* Wait for debugger? */
+       if (ppc7d_wait_debugger) {
+               printk("Waiting for debugger...\n");
+
+               while (readl(&ppc7d_wait_debugger)) ;
+       }
+
+       /* Hook up i8259 interrupt which is connected to GPP28 */
+       request_irq(mv64360_irq_base + MV64x60_IRQ_GPP28, ppc7d_i8259_intr,
+                   SA_INTERRUPT, "I8259 (GPP28) interrupt", (void *)0);
+
+       /* Configure MPP16 as watchdog NMI, MPP17 as watchdog WDE */
+       spin_lock_irqsave(&mv64x60_lock, flags);
+       data = mv64x60_read(&bh, MV64x60_MPP_CNTL_2);
+       data &= ~(0x0000000f << 0);
+       data |= (0x00000004 << 0);
+       data &= ~(0x0000000f << 4);
+       data |= (0x00000004 << 4);
+       mv64x60_write(&bh, MV64x60_MPP_CNTL_2, data);
+       spin_unlock_irqrestore(&mv64x60_lock, flags);
+
+       /* All LEDs off */
+       data8 = inb(PPC7D_CPLD_LEDS);
+       data8 &= ~0x08;
+       data8 |= 0x07;
+       outb(data8, PPC7D_CPLD_LEDS);
+
+        /* Hook up RTC. We couldn't do this earlier because we need the I2C subsystem */
+        ppc_md.set_rtc_time = ppc7d_set_rtc_time;
+        ppc_md.get_rtc_time = ppc7d_get_rtc_time;
+
+       pr_debug("%s: exit\n", __FUNCTION__);
+}
+
+/* Called from machine_init(), early, before any of the __init functions
+ * have run. We must init software-configurable pins before other functions
+ * such as interrupt controllers are initialised.
+ */
+void __init platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+                         unsigned long r6, unsigned long r7)
+{
+       u8 val8;
+       u8 rev_num;
+
+       /* Map 0xe0000000-0xffffffff early because we need access to SRAM
+        * and the ISA memory space (for serial port) here. This mapping
+        * is redone properly in ppc7d_map_io() later.
+        */
+       mtspr(SPRN_DBAT3U, 0xe0003fff);
+       mtspr(SPRN_DBAT3L, 0xe000002a);
+
+       /*
+        * Zero SRAM. Note that this generates parity errors on
+        * internal data path in SRAM if it's first time accessing it
+        * after reset.
+        *
+        * We do this ASAP to avoid parity errors when reading
+        * uninitialized SRAM.
+        */
+       memset((void *)PPC7D_INTERNAL_SRAM_BASE, 0, MV64360_SRAM_SIZE);
+
+       pr_debug("platform_init: r3-r7: %lx %lx %lx %lx %lx\n",
+                r3, r4, r5, r6, r7);
+
+       parse_bootinfo(find_bootinfo());
+
+       /* ASSUMPTION:  If both r3 (bd_t pointer) and r6 (cmdline pointer)
+        * are non-zero, then we should use the board info from the bd_t
+        * structure and the cmdline pointed to by r6 instead of the
+        * information from birecs, if any.  Otherwise, use the information
+        * from birecs as discovered by the preceeding call to
+        * parse_bootinfo().  This rule should work with both PPCBoot, which
+        * uses a bd_t board info structure, and the kernel boot wrapper,
+        * which uses birecs.
+        */
+       if (r3 && r6) {
+               bd_t *bp = (bd_t *) __res;
+
+               /* copy board info structure */
+               memcpy((void *)__res, (void *)(r3 + KERNELBASE), sizeof(bd_t));
+               /* copy command line */
+               *(char *)(r7 + KERNELBASE) = 0;
+               strcpy(cmd_line, (char *)(r6 + KERNELBASE));
+
+               printk(KERN_INFO "Board info data:-\n");
+               printk(KERN_INFO "  Internal freq: %lu MHz, bus freq: %lu MHz\n",
+                      bp->bi_intfreq, bp->bi_busfreq);
+               printk(KERN_INFO "  Memory: %lx, size %lx\n", bp->bi_memstart,
+                      bp->bi_memsize);
+               printk(KERN_INFO "  Console baudrate: %lu\n", bp->bi_baudrate);
+               printk(KERN_INFO "  Ethernet address: "
+                      "%02x:%02x:%02x:%02x:%02x:%02x\n",
+                      bp->bi_enetaddr[0], bp->bi_enetaddr[1],
+                      bp->bi_enetaddr[2], bp->bi_enetaddr[3],
+                      bp->bi_enetaddr[4], bp->bi_enetaddr[5]);
+       }
+#ifdef CONFIG_BLK_DEV_INITRD
+       /* take care of initrd if we have one */
+       if (r4) {
+               initrd_start = r4 + KERNELBASE;
+               initrd_end = r5 + KERNELBASE;
+               printk(KERN_INFO "INITRD @ %lx/%lx\n", initrd_start, initrd_end);
+       }
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+       /* Map in board regs, etc. */
+       isa_io_base = 0xe8000000;
+       isa_mem_base = 0xe8000000;
+       pci_dram_offset = 0x00000000;
+       ISA_DMA_THRESHOLD = 0x00ffffff;
+       DMA_MODE_READ = 0x44;
+       DMA_MODE_WRITE = 0x48;
+
+       ppc_md.setup_arch = ppc7d_setup_arch;
+       ppc_md.init = ppc7d_init2;
+       ppc_md.show_cpuinfo = ppc7d_show_cpuinfo;
+       ppc_md.irq_canonicalize = ppc7d_irq_canonicalize;
+       ppc_md.init_IRQ = ppc7d_init_irq;
+       ppc_md.get_irq = ppc7d_get_irq;
+
+       ppc_md.restart = ppc7d_restart;
+       ppc_md.power_off = ppc7d_power_off;
+       ppc_md.halt = ppc7d_halt;
+
+       ppc_md.find_end_of_memory = ppc7d_find_end_of_memory;
+       ppc_md.setup_io_mappings = ppc7d_map_io;
+
+       ppc_md.time_init = NULL;
+       ppc_md.set_rtc_time = NULL;
+       ppc_md.get_rtc_time = NULL;
+       ppc_md.calibrate_decr = ppc7d_calibrate_decr;
+       ppc_md.nvram_read_val = NULL;
+       ppc_md.nvram_write_val = NULL;
+
+       ppc_md.heartbeat = ppc7d_heartbeat;
+       ppc_md.heartbeat_reset = HZ;
+       ppc_md.heartbeat_count = ppc_md.heartbeat_reset;
+
+       ppc_md.pcibios_fixup_bus = ppc7d_pci_fixup_bus;
+
+#if defined(CONFIG_SERIAL_MPSC) || defined(CONFIG_MV643XX_ETH) || \
+    defined(CONFIG_I2C_MV64XXX)
+       platform_notify = ppc7d_platform_notify;
+#endif
+
+#ifdef CONFIG_SERIAL_MPSC
+       /* On PPC7D, we must configure MPSC support via CPLD control
+        * registers.
+        */
+       outb(PPC7D_CPLD_RTS_COM4_SCLK |
+            PPC7D_CPLD_RTS_COM56_ENABLED, PPC7D_CPLD_RTS);
+       outb(PPC7D_CPLD_COMS_COM3_TCLKEN |
+            PPC7D_CPLD_COMS_COM3_TXEN |
+            PPC7D_CPLD_COMS_COM4_TCLKEN |
+            PPC7D_CPLD_COMS_COM4_TXEN, PPC7D_CPLD_COMS);
+#endif /* CONFIG_SERIAL_MPSC */
+
+#if defined(CONFIG_KGDB) || defined(CONFIG_SERIAL_TEXT_DEBUG)
+       ppc7d_early_serial_map();
+#ifdef  CONFIG_SERIAL_TEXT_DEBUG
+#if defined(CONFIG_SERIAL_MPSC_CONSOLE)
+       ppc_md.progress = mv64x60_mpsc_progress;
+#elif defined(CONFIG_SERIAL_8250)
+       ppc_md.progress = gen550_progress;
+#else
+#error CONFIG_KGDB || CONFIG_SERIAL_TEXT_DEBUG has no supported CONFIG_SERIAL_XXX
+#endif /* CONFIG_SERIAL_8250 */
+#endif /* CONFIG_SERIAL_TEXT_DEBUG */
+#endif /* CONFIG_KGDB || CONFIG_SERIAL_TEXT_DEBUG */
+
+       /* Enable write access to user flash.  This is necessary for
+        * flash probe.
+        */
+       val8 = readb((void *)isa_io_base + PPC7D_CPLD_SW_FLASH_WRITE_PROTECT);
+       writeb(val8 | (PPC7D_CPLD_SW_FLASH_WRPROT_ENABLED &
+                      PPC7D_CPLD_SW_FLASH_WRPROT_USER_MASK),
+              (void *)isa_io_base + PPC7D_CPLD_SW_FLASH_WRITE_PROTECT);
+
+       /* Determine if this board has IBM ALMA VME devices */
+       val8 = readb((void *)isa_io_base + PPC7D_CPLD_BOARD_REVISION);
+       rev_num = (val8 & PPC7D_CPLD_BOARD_REVISION_NUMBER_MASK) >> 5;
+       if (rev_num <= 1)
+               ppc7d_has_alma = 1;
+
+#ifdef DEBUG
+       console_printk[0] = 8;
+#endif
+}
diff --git a/arch/ppc/platforms/radstone_ppc7d.h b/arch/ppc/platforms/radstone_ppc7d.h
new file mode 100644 (file)
index 0000000..9383755
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * arch/ppc/platforms/radstone_ppc7d.h
+ *
+ * Board definitions for the Radstone PPC7D boards.
+ *
+ * Author: James Chapman <jchapman@katalix.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
+ *    0xff000000-0xff000fff     - AFIX registers (DevCS2)
+ *    0xfef00000-0xfef0ffff      - Internal MV64x60 registers
+ *    0xfef40000-0xfef7ffff      - Internal SRAM
+ *    0xfef00000-0xfef0ffff      - MV64360 Registers
+ *    0x70000000-0x7fffffff      - soldered flash (DevCS3)
+ *    0xe8000000-0xe9ffffff      - PCI I/O
+ *    0x80000000-0xbfffffff      - PCI MEM
+ */
+
+#ifndef __PPC_PLATFORMS_PPC7D_H
+#define __PPC_PLATFORMS_PPC7D_H
+
+#include <asm/ppcboot.h>
+
+/*****************************************************************************
+ * CPU Physical Memory Map setup.
+ *****************************************************************************/
+
+#define PPC7D_BOOT_WINDOW_BASE                 0xff800000
+#define PPC7D_AFIX_REG_BASE                    0xff000000
+#define PPC7D_INTERNAL_SRAM_BASE               0xfef40000
+#define PPC7D_FLASH_BASE                       0x70000000
+
+#define PPC7D_BOOT_WINDOW_SIZE_ACTUAL          0x00800000 /* 8MB */
+#define PPC7D_FLASH_SIZE_ACTUAL                        0x10000000 /* 256MB */
+
+#define PPC7D_BOOT_WINDOW_SIZE         max(MV64360_WINDOW_SIZE_MIN,    \
+               PPC7D_BOOT_WINDOW_SIZE_ACTUAL)
+#define PPC7D_FLASH_SIZE               max(MV64360_WINDOW_SIZE_MIN,    \
+               PPC7D_FLASH_SIZE_ACTUAL)
+#define PPC7D_AFIX_REG_SIZE            max(MV64360_WINDOW_SIZE_MIN, 0xff)
+
+
+#define PPC7D_PCI0_MEM0_START_PROC_ADDR        0x80000000UL
+#define PPC7D_PCI0_MEM0_START_PCI_HI_ADDR      0x00000000UL
+#define PPC7D_PCI0_MEM0_START_PCI_LO_ADDR      0x80000000UL
+#define PPC7D_PCI0_MEM0_SIZE                   0x20000000UL
+#define PPC7D_PCI0_MEM1_START_PROC_ADDR        0xe8010000UL
+#define PPC7D_PCI0_MEM1_START_PCI_HI_ADDR      0x00000000UL
+#define PPC7D_PCI0_MEM1_START_PCI_LO_ADDR      0x00000000UL
+#define PPC7D_PCI0_MEM1_SIZE                   0x000f0000UL
+#define PPC7D_PCI0_IO_START_PROC_ADDR          0xe8000000UL
+#define PPC7D_PCI0_IO_START_PCI_ADDR           0x00000000UL
+#define PPC7D_PCI0_IO_SIZE                     0x00010000UL
+
+#define PPC7D_PCI1_MEM0_START_PROC_ADDR        0xa0000000UL
+#define PPC7D_PCI1_MEM0_START_PCI_HI_ADDR      0x00000000UL
+#define PPC7D_PCI1_MEM0_START_PCI_LO_ADDR      0xa0000000UL
+#define PPC7D_PCI1_MEM0_SIZE                   0x20000000UL
+#define PPC7D_PCI1_MEM1_START_PROC_ADDR        0xe9800000UL
+#define PPC7D_PCI1_MEM1_START_PCI_HI_ADDR      0x00000000UL
+#define PPC7D_PCI1_MEM1_START_PCI_LO_ADDR      0x00000000UL
+#define PPC7D_PCI1_MEM1_SIZE                   0x00800000UL
+#define PPC7D_PCI1_IO_START_PROC_ADDR          0xe9000000UL
+#define PPC7D_PCI1_IO_START_PCI_ADDR           0x00000000UL
+#define PPC7D_PCI1_IO_SIZE                     0x00010000UL
+
+#define        PPC7D_DEFAULT_BAUD                      9600
+#define        PPC7D_MPSC_CLK_SRC                      8         /* TCLK */
+#define        PPC7D_MPSC_CLK_FREQ                     133333333 /* 133.3333... MHz */
+
+#define        PPC7D_ETH0_PHY_ADDR                     8
+#define        PPC7D_ETH1_PHY_ADDR                     9
+#define        PPC7D_ETH2_PHY_ADDR                     0
+
+#define PPC7D_ETH_TX_QUEUE_SIZE                        400
+#define PPC7D_ETH_RX_QUEUE_SIZE                        400
+
+#define        PPC7D_ETH_PORT_CONFIG_VALUE                     \
+       MV64340_ETH_UNICAST_NORMAL_MODE                 |       \
+       MV64340_ETH_DEFAULT_RX_QUEUE_0                  |       \
+       MV64340_ETH_DEFAULT_RX_ARP_QUEUE_0              |       \
+       MV64340_ETH_RECEIVE_BC_IF_NOT_IP_OR_ARP         |       \
+       MV64340_ETH_RECEIVE_BC_IF_IP                    |       \
+       MV64340_ETH_RECEIVE_BC_IF_ARP                   |       \
+       MV64340_ETH_CAPTURE_TCP_FRAMES_DIS              |       \
+       MV64340_ETH_CAPTURE_UDP_FRAMES_DIS              |       \
+       MV64340_ETH_DEFAULT_RX_TCP_QUEUE_0              |       \
+       MV64340_ETH_DEFAULT_RX_UDP_QUEUE_0              |       \
+       MV64340_ETH_DEFAULT_RX_BPDU_QUEUE_0
+
+#define        PPC7D_ETH_PORT_CONFIG_EXTEND_VALUE              \
+       MV64340_ETH_SPAN_BPDU_PACKETS_AS_NORMAL         |       \
+       MV64340_ETH_PARTITION_DISABLE
+
+#define        GT_ETH_IPG_INT_RX(value)                        \
+       ((value & 0x3fff) << 8)
+
+#define        PPC7D_ETH_PORT_SDMA_CONFIG_VALUE                \
+       MV64340_ETH_RX_BURST_SIZE_4_64BIT               |       \
+       GT_ETH_IPG_INT_RX(0)                    |       \
+       MV64340_ETH_TX_BURST_SIZE_4_64BIT
+
+#define        PPC7D_ETH_PORT_SERIAL_CONTROL_VALUE             \
+       MV64340_ETH_ENABLE_AUTO_NEG_FOR_DUPLX           |       \
+       MV64340_ETH_DISABLE_AUTO_NEG_FOR_FLOW_CTRL      |       \
+       MV64340_ETH_ADV_SYMMETRIC_FLOW_CTRL             |       \
+       MV64340_ETH_FORCE_FC_MODE_NO_PAUSE_DIS_TX       |       \
+       MV64340_ETH_FORCE_BP_MODE_NO_JAM                |       \
+       (1 << 9)                                        |       \
+       MV64340_ETH_DO_NOT_FORCE_LINK_FAIL              |       \
+       MV64340_ETH_RETRANSMIT_16_ATTEMPTS              |       \
+       MV64340_ETH_ENABLE_AUTO_NEG_SPEED_GMII          |       \
+       MV64340_ETH_DTE_ADV_0                           |       \
+       MV64340_ETH_DISABLE_AUTO_NEG_BYPASS             |       \
+       MV64340_ETH_AUTO_NEG_NO_CHANGE                  |       \
+       MV64340_ETH_MAX_RX_PACKET_9700BYTE              |       \
+       MV64340_ETH_CLR_EXT_LOOPBACK                    |       \
+       MV64340_ETH_SET_FULL_DUPLEX_MODE                |       \
+       MV64340_ETH_ENABLE_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX
+
+/*****************************************************************************
+ * Serial defines.
+ *****************************************************************************/
+
+#define PPC7D_SERIAL_0         0xe80003f8
+#define PPC7D_SERIAL_1         0xe80002f8
+
+#define RS_TABLE_SIZE  2
+
+/* Rate for the 1.8432 Mhz clock for the onboard serial chip */
+#define UART_CLK                       1843200
+#define BASE_BAUD                      ( UART_CLK / 16 )
+
+#ifdef CONFIG_SERIAL_DETECT_IRQ
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ)
+#else
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF)
+#endif
+
+#define STD_SERIAL_PORT_DFNS \
+        { 0, BASE_BAUD, PPC7D_SERIAL_0, 4, STD_COM_FLAGS, /* ttyS0 */ \
+               iomem_base: (u8 *)PPC7D_SERIAL_0,                         \
+               io_type: SERIAL_IO_MEM, },                                \
+        { 0, BASE_BAUD, PPC7D_SERIAL_1, 3, STD_COM_FLAGS, /* ttyS1 */ \
+               iomem_base: (u8 *)PPC7D_SERIAL_1,                         \
+               io_type: SERIAL_IO_MEM },
+
+#define SERIAL_PORT_DFNS \
+        STD_SERIAL_PORT_DFNS
+
+/*****************************************************************************
+ * CPLD defines.
+ *
+ * Register map:-
+ *
+ * 0000 to 000F        South Bridge DMA 1 Control
+ * 0020 and 0021       South Bridge Interrupt 1 Control
+ * 0040 to 0043        South Bridge Counter Control
+ * 0060                Keyboard
+ * 0061                South Bridge NMI Status and Control
+ * 0064                Keyboard
+ * 0071 and 0072       RTC R/W
+ * 0078 to 007B        South Bridge BIOS Timer
+ * 0080 to 0090        South Bridge DMA Pages
+ * 00A0 and 00A1       South Bridge Interrupt 2 Control
+ * 00C0 to 00DE        South Bridge DMA 2 Control
+ * 02E8 to 02EF        COM6 R/W
+ * 02F8 to 02FF        South Bridge COM2 R/W
+ * 03E8 to 03EF        COM5 R/W
+ * 03F8 to 03FF        South Bridge COM1 R/W
+ * 040A                South Bridge DMA Scatter/Gather RO
+ * 040B                DMA 1 Extended Mode WO
+ * 0410 to 043F        South Bridge DMA Scatter/Gather
+ * 0481 to 048B        South Bridge DMA High Pages
+ * 04D0 and 04D1       South Bridge Edge/Level Control
+ * 04D6                DMA 2 Extended Mode WO
+ * 0804                Memory Configuration RO
+ * 0806                Memory Configuration Extend RO
+ * 0808                SCSI Activity LED R/W
+ * 080C                Equipment Present 1 RO
+ * 080E                Equipment Present 2 RO
+ * 0810                Equipment Present 3 RO
+ * 0812                Equipment Present 4 RO
+ * 0818                Key Lock RO
+ * 0820                LEDS R/W
+ * 0824                COMs R/W
+ * 0826                RTS R/W
+ * 0828                Reset R/W
+ * 082C                Watchdog Trig R/W
+ * 082E                Interrupt R/W
+ * 0830                Interrupt Status RO
+ * 0832                PCI configuration RO
+ * 0854                Board Revision RO
+ * 0858                Extended ID RO
+ * 0864                ID Link RO
+ * 0866                Motherboard Type RO
+ * 0868                FLASH Write control RO
+ * 086A                Software FLASH write protect R/W
+ * 086E                FLASH Control R/W
+ *****************************************************************************/
+
+#define PPC7D_CPLD_MEM_CONFIG                  0x0804
+#define PPC7D_CPLD_MEM_CONFIG_EXTEND           0x0806
+#define PPC7D_CPLD_SCSI_ACTIVITY_LED           0x0808
+#define PPC7D_CPLD_EQUIPMENT_PRESENT_1         0x080C
+#define PPC7D_CPLD_EQUIPMENT_PRESENT_2         0x080E
+#define PPC7D_CPLD_EQUIPMENT_PRESENT_3         0x0810
+#define PPC7D_CPLD_EQUIPMENT_PRESENT_4         0x0812
+#define PPC7D_CPLD_KEY_LOCK                    0x0818
+#define PPC7D_CPLD_LEDS                                0x0820
+#define PPC7D_CPLD_COMS                                0x0824
+#define PPC7D_CPLD_RTS                         0x0826
+#define PPC7D_CPLD_RESET                       0x0828
+#define PPC7D_CPLD_WATCHDOG_TRIG               0x082C
+#define PPC7D_CPLD_INTR                                0x082E
+#define PPC7D_CPLD_INTR_STATUS                 0x0830
+#define PPC7D_CPLD_PCI_CONFIG                  0x0832
+#define PPC7D_CPLD_BOARD_REVISION              0x0854
+#define PPC7D_CPLD_EXTENDED_ID                 0x0858
+#define PPC7D_CPLD_ID_LINK                     0x0864
+#define PPC7D_CPLD_MOTHERBOARD_TYPE            0x0866
+#define PPC7D_CPLD_FLASH_WRITE_CNTL            0x0868
+#define PPC7D_CPLD_SW_FLASH_WRITE_PROTECT      0x086A
+#define PPC7D_CPLD_FLASH_CNTL                  0x086E
+
+/* MEMORY_CONFIG_EXTEND */
+#define PPC7D_CPLD_SDRAM_BANK_NUM_MASK         0x02
+#define PPC7D_CPLD_SDRAM_BANK_SIZE_MASK                0xc0
+#define PPC7D_CPLD_SDRAM_BANK_SIZE_128M                0
+#define PPC7D_CPLD_SDRAM_BANK_SIZE_256M                0x40
+#define PPC7D_CPLD_SDRAM_BANK_SIZE_512M                0x80
+#define PPC7D_CPLD_SDRAM_BANK_SIZE_1G          0xc0
+#define PPC7D_CPLD_FLASH_DEV_SIZE_MASK         0x03
+#define PPC7D_CPLD_FLASH_BANK_NUM_MASK         0x0c
+#define PPC7D_CPLD_FLASH_DEV_SIZE_64M          0
+#define PPC7D_CPLD_FLASH_DEV_SIZE_32M          1
+#define PPC7D_CPLD_FLASH_DEV_SIZE_16M          3
+#define PPC7D_CPLD_FLASH_BANK_NUM_4            0x00
+#define PPC7D_CPLD_FLASH_BANK_NUM_3            0x04
+#define PPC7D_CPLD_FLASH_BANK_NUM_2            0x08
+#define PPC7D_CPLD_FLASH_BANK_NUM_1            0x0c
+
+/* SCSI_LED */
+#define PPC7D_CPLD_SCSI_ACTIVITY_LED_OFF       0
+#define PPC7D_CPLD_SCSI_ACTIVITY_LED_ON                1
+
+/* EQUIPMENT_PRESENT_1 */
+#define PPC7D_CPLD_EQPT_PRES_1_FITTED          0
+#define PPC7D_CPLD_EQPT_PRES_1_PMC2_MASK       (0x80 >> 2)
+#define PPC7D_CPLD_EQPT_PRES_1_PMC1_MASK       (0x80 >> 3)
+#define PPC7D_CPLD_EQPT_PRES_1_AFIX_MASK       (0x80 >> 4)
+
+/* EQUIPMENT_PRESENT_2 */
+#define PPC7D_CPLD_EQPT_PRES_2_FITTED          !0
+#define PPC7D_CPLD_EQPT_PRES_2_UNIVERSE_MASK   (0x80 >> 0)
+#define PPC7D_CPLD_EQPT_PRES_2_COM36_MASK      (0x80 >> 2)
+#define PPC7D_CPLD_EQPT_PRES_2_GIGE_MASK       (0x80 >> 3)
+#define PPC7D_CPLD_EQPT_PRES_2_DUALGIGE_MASK   (0x80 >> 4)
+
+/* EQUIPMENT_PRESENT_3 */
+#define PPC7D_CPLD_EQPT_PRES_3_PMC2_V_MASK     (0x80 >> 3)
+#define PPC7D_CPLD_EQPT_PRES_3_PMC2_5V         (0 >> 3)
+#define PPC7D_CPLD_EQPT_PRES_3_PMC2_3V         (0x80 >> 3)
+#define PPC7D_CPLD_EQPT_PRES_3_PMC1_V_MASK     (0x80 >> 4)
+#define PPC7D_CPLD_EQPT_PRES_3_PMC1_5V         (0 >> 4)
+#define PPC7D_CPLD_EQPT_PRES_3_PMC1_3V         (0x80 >> 4)
+#define PPC7D_CPLD_EQPT_PRES_3_PMC_POWER_MASK  (0x80 >> 5)
+#define PPC7D_CPLD_EQPT_PRES_3_PMC_POWER_INTER (0 >> 5)
+#define PPC7D_CPLD_EQPT_PRES_3_PMC_POWER_VME   (0x80 >> 5)
+
+/* EQUIPMENT_PRESENT_4 */
+#define PPC7D_CPLD_EQPT_PRES_4_LPT_MASK                (0x80 >> 2)
+#define PPC7D_CPLD_EQPT_PRES_4_LPT_FITTED      (0x80 >> 2)
+#define PPC7D_CPLD_EQPT_PRES_4_PS2_USB2_MASK   (0xc0 >> 6)
+#define PPC7D_CPLD_EQPT_PRES_4_PS2_FITTED      (0x40 >> 6)
+#define PPC7D_CPLD_EQPT_PRES_4_USB2_FITTED     (0x80 >> 6)
+
+/* CPLD_LEDS */
+#define PPC7D_CPLD_LEDS_ON                     (!0)
+#define PPC7D_CPLD_LEDS_OFF                    (0)
+#define PPC7D_CPLD_LEDS_NVRAM_PAGE_MASK                (0xc0 >> 2)
+#define PPC7D_CPLD_LEDS_DS201_MASK             (0x80 >> 4)
+#define PPC7D_CPLD_LEDS_DS219_MASK             (0x80 >> 5)
+#define PPC7D_CPLD_LEDS_DS220_MASK             (0x80 >> 6)
+#define PPC7D_CPLD_LEDS_DS221_MASK             (0x80 >> 7)
+
+/* CPLD_COMS */
+#define PPC7D_CPLD_COMS_COM3_TCLKEN            (0x80 >> 0)
+#define PPC7D_CPLD_COMS_COM3_RTCLKEN           (0x80 >> 1)
+#define PPC7D_CPLD_COMS_COM3_MODE_MASK         (0x80 >> 2)
+#define PPC7D_CPLD_COMS_COM3_MODE_RS232                (0)
+#define PPC7D_CPLD_COMS_COM3_MODE_RS422                (0x80 >> 2)
+#define PPC7D_CPLD_COMS_COM3_TXEN              (0x80 >> 3)
+#define PPC7D_CPLD_COMS_COM4_TCLKEN            (0x80 >> 4)
+#define PPC7D_CPLD_COMS_COM4_RTCLKEN           (0x80 >> 5)
+#define PPC7D_CPLD_COMS_COM4_MODE_MASK         (0x80 >> 6)
+#define PPC7D_CPLD_COMS_COM4_MODE_RS232                (0)
+#define PPC7D_CPLD_COMS_COM4_MODE_RS422                (0x80 >> 6)
+#define PPC7D_CPLD_COMS_COM4_TXEN              (0x80 >> 7)
+
+/* CPLD_RTS */
+#define PPC7D_CPLD_RTS_COM36_LOOPBACK          (0x80 >> 0)
+#define PPC7D_CPLD_RTS_COM4_SCLK               (0x80 >> 1)
+#define PPC7D_CPLD_RTS_COM3_TXFUNC_MASK                (0xc0 >> 2)
+#define PPC7D_CPLD_RTS_COM3_TXFUNC_DISABLED    (0 >> 2)
+#define PPC7D_CPLD_RTS_COM3_TXFUNC_ENABLED     (0x80 >> 2)
+#define PPC7D_CPLD_RTS_COM3_TXFUNC_ENABLED_RTG3        (0xc0 >> 2)
+#define PPC7D_CPLD_RTS_COM3_TXFUNC_ENABLED_RTG3S (0xc0 >> 2)
+#define PPC7D_CPLD_RTS_COM56_MODE_MASK         (0x80 >> 4)
+#define PPC7D_CPLD_RTS_COM56_MODE_RS232                (0)
+#define PPC7D_CPLD_RTS_COM56_MODE_RS422                (0x80 >> 4)
+#define PPC7D_CPLD_RTS_COM56_ENABLE_MASK       (0x80 >> 5)
+#define PPC7D_CPLD_RTS_COM56_DISABLED          (0)
+#define PPC7D_CPLD_RTS_COM56_ENABLED           (0x80 >> 5)
+#define PPC7D_CPLD_RTS_COM4_TXFUNC_MASK                (0xc0 >> 6)
+#define PPC7D_CPLD_RTS_COM4_TXFUNC_DISABLED    (0 >> 6)
+#define PPC7D_CPLD_RTS_COM4_TXFUNC_ENABLED     (0x80 >> 6)
+#define PPC7D_CPLD_RTS_COM4_TXFUNC_ENABLED_RTG3        (0x40 >> 6)
+#define PPC7D_CPLD_RTS_COM4_TXFUNC_ENABLED_RTG3S (0x40 >> 6)
+
+/* WATCHDOG_TRIG */
+#define PPC7D_CPLD_WDOG_CAUSE_MASK             (0x80 >> 0)
+#define PPC7D_CPLD_WDOG_CAUSE_NORMAL_RESET     (0 >> 0)
+#define PPC7D_CPLD_WDOG_CAUSE_WATCHDOG         (0x80 >> 0)
+#define PPC7D_CPLD_WDOG_ENABLE_MASK            (0x80 >> 6)
+#define PPC7D_CPLD_WDOG_ENABLE_OFF             (0 >> 6)
+#define PPC7D_CPLD_WDOG_ENABLE_ON              (0x80 >> 6)
+#define PPC7D_CPLD_WDOG_RESETSW_MASK           (0x80 >> 7)
+#define PPC7D_CPLD_WDOG_RESETSW_OFF            (0 >> 7)
+#define PPC7D_CPLD_WDOG_RESETSW_ON             (0x80 >> 7)
+
+/* Interrupt mask and status bits */
+#define PPC7D_CPLD_INTR_TEMP_MASK              (0x80 >> 0)
+#define PPC7D_CPLD_INTR_HB8_MASK               (0x80 >> 1)
+#define PPC7D_CPLD_INTR_PHY1_MASK              (0x80 >> 2)
+#define PPC7D_CPLD_INTR_PHY0_MASK              (0x80 >> 3)
+#define PPC7D_CPLD_INTR_ISANMI_MASK            (0x80 >> 5)
+#define PPC7D_CPLD_INTR_CRITTEMP_MASK          (0x80 >> 6)
+
+/* CPLD_INTR */
+#define PPC7D_CPLD_INTR_ENABLE_OFF             (0)
+#define PPC7D_CPLD_INTR_ENABLE_ON              (!0)
+
+/* CPLD_INTR_STATUS */
+#define PPC7D_CPLD_INTR_STATUS_OFF             (0)
+#define PPC7D_CPLD_INTR_STATUS_ON              (!0)
+
+/* CPLD_PCI_CONFIG */
+#define PPC7D_CPLD_PCI_CONFIG_PCI0_MASK                0x70
+#define PPC7D_CPLD_PCI_CONFIG_PCI0_PCI33       0x00
+#define PPC7D_CPLD_PCI_CONFIG_PCI0_PCI66       0x10
+#define PPC7D_CPLD_PCI_CONFIG_PCI0_PCIX33      0x40
+#define PPC7D_CPLD_PCI_CONFIG_PCI0_PCIX66      0x50
+#define PPC7D_CPLD_PCI_CONFIG_PCI0_PCIX100      0x60
+#define PPC7D_CPLD_PCI_CONFIG_PCI0_PCIX133     0x70
+#define PPC7D_CPLD_PCI_CONFIG_PCI1_MASK                0x07
+#define PPC7D_CPLD_PCI_CONFIG_PCI1_PCI33       0x00
+#define PPC7D_CPLD_PCI_CONFIG_PCI1_PCI66       0x01
+#define PPC7D_CPLD_PCI_CONFIG_PCI1_PCIX33      0x04
+#define PPC7D_CPLD_PCI_CONFIG_PCI1_PCIX66      0x05
+#define PPC7D_CPLD_PCI_CONFIG_PCI1_PCIX100     0x06
+#define PPC7D_CPLD_PCI_CONFIG_PCI1_PCIX133     0x07
+
+/* CPLD_BOARD_REVISION */
+#define PPC7D_CPLD_BOARD_REVISION_NUMBER_MASK  0xe0
+#define PPC7D_CPLD_BOARD_REVISION_LETTER_MASK  0x1f
+
+/* CPLD_EXTENDED_ID */
+#define PPC7D_CPLD_EXTENDED_ID_PPC7D           0x18
+
+/* CPLD_ID_LINK */
+#define PPC7D_CPLD_ID_LINK_VME64_GAP_MASK      (0x80 >> 2)
+#define PPC7D_CPLD_ID_LINK_VME64_GA4_MASK      (0x80 >> 3)
+#define PPC7D_CPLD_ID_LINK_E13_MASK            (0x80 >> 4)
+#define PPC7D_CPLD_ID_LINK_E12_MASK            (0x80 >> 5)
+#define PPC7D_CPLD_ID_LINK_E7_MASK             (0x80 >> 6)
+#define PPC7D_CPLD_ID_LINK_E6_MASK             (0x80 >> 7)
+
+/* CPLD_MOTHERBOARD_TYPE */
+#define PPC7D_CPLD_MB_TYPE_ECC_ENABLE_MASK     (0x80 >> 0)
+#define PPC7D_CPLD_MB_TYPE_ECC_ENABLED         (0x80 >> 0)
+#define PPC7D_CPLD_MB_TYPE_ECC_DISABLED                (0 >> 0)
+#define PPC7D_CPLD_MB_TYPE_ECC_FITTED_MASK     (0x80 >> 3)
+#define PPC7D_CPLD_MB_TYPE_PLL_MASK            0x0c
+#define PPC7D_CPLD_MB_TYPE_PLL_133             0x00
+#define PPC7D_CPLD_MB_TYPE_PLL_100             0x08
+#define PPC7D_CPLD_MB_TYPE_PLL_64              0x04
+#define PPC7D_CPLD_MB_TYPE_HW_ID_MASK          0x03
+
+/* CPLD_FLASH_WRITE_CNTL */
+#define PPD7D_CPLD_FLASH_CNTL_WR_LINK_MASK     (0x80 >> 0)
+#define PPD7D_CPLD_FLASH_CNTL_WR_LINK_FITTED   (0x80 >> 0)
+#define PPD7D_CPLD_FLASH_CNTL_BOOT_LINK_MASK   (0x80 >> 2)
+#define PPD7D_CPLD_FLASH_CNTL_BOOT_LINK_FITTED (0x80 >> 2)
+#define PPD7D_CPLD_FLASH_CNTL_USER_LINK_MASK   (0x80 >> 3)
+#define PPD7D_CPLD_FLASH_CNTL_USER_LINK_FITTED (0x80 >> 3)
+#define PPD7D_CPLD_FLASH_CNTL_RECO_WR_MASK     (0x80 >> 5)
+#define PPD7D_CPLD_FLASH_CNTL_RECO_WR_ENABLED  (0x80 >> 5)
+#define PPD7D_CPLD_FLASH_CNTL_BOOT_WR_MASK     (0x80 >> 6)
+#define PPD7D_CPLD_FLASH_CNTL_BOOT_WR_ENABLED  (0x80 >> 6)
+#define PPD7D_CPLD_FLASH_CNTL_USER_WR_MASK     (0x80 >> 7)
+#define PPD7D_CPLD_FLASH_CNTL_USER_WR_ENABLED  (0x80 >> 7)
+
+/* CPLD_SW_FLASH_WRITE_PROTECT */
+#define PPC7D_CPLD_SW_FLASH_WRPROT_ENABLED     (!0)
+#define PPC7D_CPLD_SW_FLASH_WRPROT_DISABLED    (0)
+#define PPC7D_CPLD_SW_FLASH_WRPROT_SYSBOOT_MASK        (0x80 >> 6)
+#define PPC7D_CPLD_SW_FLASH_WRPROT_USER_MASK   (0x80 >> 7)
+
+/* CPLD_FLASH_WRITE_CNTL */
+#define PPC7D_CPLD_FLASH_CNTL_NVRAM_PROT_MASK  (0x80 >> 0)
+#define PPC7D_CPLD_FLASH_CNTL_NVRAM_DISABLED   (0 >> 0)
+#define PPC7D_CPLD_FLASH_CNTL_NVRAM_ENABLED    (0x80 >> 0)
+#define PPC7D_CPLD_FLASH_CNTL_ALTBOOT_LINK_MASK        (0x80 >> 1)
+#define PPC7D_CPLD_FLASH_CNTL_VMEBOOT_LINK_MASK        (0x80 >> 2)
+#define PPC7D_CPLD_FLASH_CNTL_RECBOOT_LINK_MASK        (0x80 >> 3)
+
+
+#endif /* __PPC_PLATFORMS_PPC7D_H */
diff --git a/arch/ppc/syslib/ipic.c b/arch/ppc/syslib/ipic.c
new file mode 100644 (file)
index 0000000..580ed65
--- /dev/null
@@ -0,0 +1,646 @@
+/*
+ * include/asm-ppc/ipic.c
+ *
+ * IPIC routines implementations.
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/sysdev.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/ipic.h>
+#include <asm/mpc83xx.h>
+
+#include "ipic.h"
+
+static struct ipic p_ipic;
+static struct ipic * primary_ipic;
+
+static struct ipic_info ipic_info[] = {
+       [9] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_H,
+               .prio   = IPIC_SIPRR_D,
+               .force  = IPIC_SIFCR_H,
+               .bit    = 24,
+               .prio_mask = 0,
+       },
+       [10] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_H,
+               .prio   = IPIC_SIPRR_D,
+               .force  = IPIC_SIFCR_H,
+               .bit    = 25,
+               .prio_mask = 1,
+       },
+       [11] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_H,
+               .prio   = IPIC_SIPRR_D,
+               .force  = IPIC_SIFCR_H,
+               .bit    = 26,
+               .prio_mask = 2,
+       },
+       [14] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_H,
+               .prio   = IPIC_SIPRR_D,
+               .force  = IPIC_SIFCR_H,
+               .bit    = 29,
+               .prio_mask = 5,
+       },
+       [15] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_H,
+               .prio   = IPIC_SIPRR_D,
+               .force  = IPIC_SIFCR_H,
+               .bit    = 30,
+               .prio_mask = 6,
+       },
+       [16] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_H,
+               .prio   = IPIC_SIPRR_D,
+               .force  = IPIC_SIFCR_H,
+               .bit    = 31,
+               .prio_mask = 7,
+       },
+       [17] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SEMSR,
+               .prio   = IPIC_SMPRR_A,
+               .force  = IPIC_SEFCR,
+               .bit    = 1,
+               .prio_mask = 5,
+       },
+       [18] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SEMSR,
+               .prio   = IPIC_SMPRR_A,
+               .force  = IPIC_SEFCR,
+               .bit    = 2,
+               .prio_mask = 6,
+       },
+       [19] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SEMSR,
+               .prio   = IPIC_SMPRR_A,
+               .force  = IPIC_SEFCR,
+               .bit    = 3,
+               .prio_mask = 7,
+       },
+       [20] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SEMSR,
+               .prio   = IPIC_SMPRR_B,
+               .force  = IPIC_SEFCR,
+               .bit    = 4,
+               .prio_mask = 4,
+       },
+       [21] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SEMSR,
+               .prio   = IPIC_SMPRR_B,
+               .force  = IPIC_SEFCR,
+               .bit    = 5,
+               .prio_mask = 5,
+       },
+       [22] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SEMSR,
+               .prio   = IPIC_SMPRR_B,
+               .force  = IPIC_SEFCR,
+               .bit    = 6,
+               .prio_mask = 6,
+       },
+       [23] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SEMSR,
+               .prio   = IPIC_SMPRR_B,
+               .force  = IPIC_SEFCR,
+               .bit    = 7,
+               .prio_mask = 7,
+       },
+       [32] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_H,
+               .prio   = IPIC_SIPRR_A,
+               .force  = IPIC_SIFCR_H,
+               .bit    = 0,
+               .prio_mask = 0,
+       },
+       [33] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_H,
+               .prio   = IPIC_SIPRR_A,
+               .force  = IPIC_SIFCR_H,
+               .bit    = 1,
+               .prio_mask = 1,
+       },
+       [34] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_H,
+               .prio   = IPIC_SIPRR_A,
+               .force  = IPIC_SIFCR_H,
+               .bit    = 2,
+               .prio_mask = 2,
+       },
+       [35] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_H,
+               .prio   = IPIC_SIPRR_A,
+               .force  = IPIC_SIFCR_H,
+               .bit    = 3,
+               .prio_mask = 3,
+       },
+       [36] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_H,
+               .prio   = IPIC_SIPRR_A,
+               .force  = IPIC_SIFCR_H,
+               .bit    = 4,
+               .prio_mask = 4,
+       },
+       [37] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_H,
+               .prio   = IPIC_SIPRR_A,
+               .force  = IPIC_SIFCR_H,
+               .bit    = 5,
+               .prio_mask = 5,
+       },
+       [38] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_H,
+               .prio   = IPIC_SIPRR_A,
+               .force  = IPIC_SIFCR_H,
+               .bit    = 6,
+               .prio_mask = 6,
+       },
+       [39] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_H,
+               .prio   = IPIC_SIPRR_A,
+               .force  = IPIC_SIFCR_H,
+               .bit    = 7,
+               .prio_mask = 7,
+       },
+       [48] = {
+               .pend   = IPIC_SEPNR,
+               .mask   = IPIC_SEMSR,
+               .prio   = IPIC_SMPRR_A,
+               .force  = IPIC_SEFCR,
+               .bit    = 0,
+               .prio_mask = 4,
+       },
+       [64] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = IPIC_SMPRR_A,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 0,
+               .prio_mask = 0,
+       },
+       [65] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = IPIC_SMPRR_A,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 1,
+               .prio_mask = 1,
+       },
+       [66] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = IPIC_SMPRR_A,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 2,
+               .prio_mask = 2,
+       },
+       [67] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = IPIC_SMPRR_A,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 3,
+               .prio_mask = 3,
+       },
+       [68] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = IPIC_SMPRR_B,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 4,
+               .prio_mask = 0,
+       },
+       [69] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = IPIC_SMPRR_B,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 5,
+               .prio_mask = 1,
+       },
+       [70] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = IPIC_SMPRR_B,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 6,
+               .prio_mask = 2,
+       },
+       [71] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = IPIC_SMPRR_B,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 7,
+               .prio_mask = 3,
+       },
+       [72] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = 0,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 8,
+       },
+       [73] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = 0,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 9,
+       },
+       [74] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = 0,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 10,
+       },
+       [75] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = 0,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 11,
+       },
+       [76] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = 0,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 12,
+       },
+       [77] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = 0,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 13,
+       },
+       [78] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = 0,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 14,
+       },
+       [79] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = 0,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 15,
+       },
+       [80] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = 0,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 16,
+       },
+       [84] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = 0,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 20,
+       },
+       [85] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = 0,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 21,
+       },
+       [90] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = 0,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 26,
+       },
+       [91] = {
+               .pend   = IPIC_SIPNR_H,
+               .mask   = IPIC_SIMSR_L,
+               .prio   = 0,
+               .force  = IPIC_SIFCR_L,
+               .bit    = 27,
+       },
+};
+
+static inline u32 ipic_read(volatile u32 __iomem *base, unsigned int reg)
+{
+       return in_be32(base + (reg >> 2));
+}
+
+static inline void ipic_write(volatile u32 __iomem *base, unsigned int reg, u32 value)
+{
+       out_be32(base + (reg >> 2), value);
+}
+
+static inline struct ipic * ipic_from_irq(unsigned int irq)
+{
+       return primary_ipic;
+}
+
+static void ipic_enable_irq(unsigned int irq)
+{
+       struct ipic *ipic = ipic_from_irq(irq);
+       unsigned int src = irq - ipic->irq_offset;
+       u32 temp;
+
+       temp = ipic_read(ipic->regs, ipic_info[src].mask);
+       temp |= (1 << (31 - ipic_info[src].bit));
+       ipic_write(ipic->regs, ipic_info[src].mask, temp);
+}
+
+static void ipic_disable_irq(unsigned int irq)
+{
+       struct ipic *ipic = ipic_from_irq(irq);
+       unsigned int src = irq - ipic->irq_offset;
+       u32 temp;
+
+       temp = ipic_read(ipic->regs, ipic_info[src].mask);
+       temp &= ~(1 << (31 - ipic_info[src].bit));
+       ipic_write(ipic->regs, ipic_info[src].mask, temp);
+}
+
+static void ipic_disable_irq_and_ack(unsigned int irq)
+{
+       struct ipic *ipic = ipic_from_irq(irq);
+       unsigned int src = irq - ipic->irq_offset;
+       u32 temp;
+
+       ipic_disable_irq(irq);
+
+       temp = ipic_read(ipic->regs, ipic_info[src].pend);
+       temp |= (1 << (31 - ipic_info[src].bit));
+       ipic_write(ipic->regs, ipic_info[src].pend, temp);
+}
+
+static void ipic_end_irq(unsigned int irq)
+{
+       if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+               ipic_enable_irq(irq);
+}
+
+struct hw_interrupt_type ipic = {
+       .typename = " IPIC  ",
+       .enable = ipic_enable_irq,
+       .disable = ipic_disable_irq,
+       .ack = ipic_disable_irq_and_ack,
+       .end = ipic_end_irq,
+};
+
+void __init ipic_init(phys_addr_t phys_addr,
+               unsigned int flags,
+               unsigned int irq_offset,
+               unsigned char *senses,
+               unsigned int senses_count)
+{
+       u32 i, temp = 0;
+
+       primary_ipic = &p_ipic;
+       primary_ipic->regs = ioremap(phys_addr, MPC83xx_IPIC_SIZE);
+
+       primary_ipic->irq_offset = irq_offset;
+
+       ipic_write(primary_ipic->regs, IPIC_SICNR, 0x0);
+
+       /* default priority scheme is grouped. If spread mode is required
+        * configure SICFR accordingly */
+       if (flags & IPIC_SPREADMODE_GRP_A)
+               temp |= SICFR_IPSA;
+       if (flags & IPIC_SPREADMODE_GRP_D)
+               temp |= SICFR_IPSD;
+       if (flags & IPIC_SPREADMODE_MIX_A)
+               temp |= SICFR_MPSA;
+       if (flags & IPIC_SPREADMODE_MIX_B)
+               temp |= SICFR_MPSB;
+
+       ipic_write(primary_ipic->regs, IPIC_SICNR, temp);
+
+       /* handle MCP route */
+       temp = 0;
+       if (flags & IPIC_DISABLE_MCP_OUT)
+               temp = SERCR_MCPR;
+       ipic_write(primary_ipic->regs, IPIC_SERCR, temp);
+
+       /* handle routing of IRQ0 to MCP */
+       temp = ipic_read(primary_ipic->regs, IPIC_SEMSR);
+
+       if (flags & IPIC_IRQ0_MCP)
+               temp |= SEMSR_SIRQ0;
+       else
+               temp &= ~SEMSR_SIRQ0;
+
+       ipic_write(primary_ipic->regs, IPIC_SEMSR, temp);
+
+       for (i = 0 ; i < NR_IPIC_INTS ; i++) {
+               irq_desc[i+irq_offset].handler = &ipic;
+               irq_desc[i+irq_offset].status = IRQ_LEVEL;
+       }
+
+       temp = 0;
+       for (i = 0 ; i < senses_count ; i++) {
+               if ((senses[i] & IRQ_SENSE_MASK) == IRQ_SENSE_EDGE) {
+                       temp |= 1 << (15 - i);
+                       if (i != 0)
+                               irq_desc[i + irq_offset + MPC83xx_IRQ_EXT1 - 1].status = 0;
+                       else
+                               irq_desc[irq_offset + MPC83xx_IRQ_EXT0].status = 0;
+               }
+       }
+       ipic_write(primary_ipic->regs, IPIC_SECNR, temp);
+
+       printk ("IPIC (%d IRQ sources, %d External IRQs) at %p\n", NR_IPIC_INTS,
+                       senses_count, primary_ipic->regs);
+}
+
+int ipic_set_priority(unsigned int irq, unsigned int priority)
+{
+       struct ipic *ipic = ipic_from_irq(irq);
+       unsigned int src = irq - ipic->irq_offset;
+       u32 temp;
+
+       if (priority > 7)
+               return -EINVAL;
+       if (src > 127)
+               return -EINVAL;
+       if (ipic_info[src].prio == 0)
+               return -EINVAL;
+
+       temp = ipic_read(ipic->regs, ipic_info[src].prio);
+
+       if (priority < 4) {
+               temp &= ~(0x7 << (20 + (3 - priority) * 3));
+               temp |= ipic_info[src].prio_mask << (20 + (3 - priority) * 3);
+       } else {
+               temp &= ~(0x7 << (4 + (7 - priority) * 3));
+               temp |= ipic_info[src].prio_mask << (4 + (7 - priority) * 3);
+       }
+
+       ipic_write(ipic->regs, ipic_info[src].prio, temp);
+
+       return 0;
+}
+
+void ipic_set_highest_priority(unsigned int irq)
+{
+       struct ipic *ipic = ipic_from_irq(irq);
+       unsigned int src = irq - ipic->irq_offset;
+       u32 temp;
+
+       temp = ipic_read(ipic->regs, IPIC_SICFR);
+
+       /* clear and set HPI */
+       temp &= 0x7f000000;
+       temp |= (src & 0x7f) << 24;
+
+       ipic_write(ipic->regs, IPIC_SICFR, temp);
+}
+
+void ipic_set_default_priority(void)
+{
+       ipic_set_priority(MPC83xx_IRQ_TSEC1_TX, 0);
+       ipic_set_priority(MPC83xx_IRQ_TSEC1_RX, 1);
+       ipic_set_priority(MPC83xx_IRQ_TSEC1_ERROR, 2);
+       ipic_set_priority(MPC83xx_IRQ_TSEC2_TX, 3);
+       ipic_set_priority(MPC83xx_IRQ_TSEC2_RX, 4);
+       ipic_set_priority(MPC83xx_IRQ_TSEC2_ERROR, 5);
+       ipic_set_priority(MPC83xx_IRQ_USB2_DR, 6);
+       ipic_set_priority(MPC83xx_IRQ_USB2_MPH, 7);
+
+       ipic_set_priority(MPC83xx_IRQ_UART1, 0);
+       ipic_set_priority(MPC83xx_IRQ_UART2, 1);
+       ipic_set_priority(MPC83xx_IRQ_SEC2, 2);
+       ipic_set_priority(MPC83xx_IRQ_IIC1, 5);
+       ipic_set_priority(MPC83xx_IRQ_IIC2, 6);
+       ipic_set_priority(MPC83xx_IRQ_SPI, 7);
+       ipic_set_priority(MPC83xx_IRQ_RTC_SEC, 0);
+       ipic_set_priority(MPC83xx_IRQ_PIT, 1);
+       ipic_set_priority(MPC83xx_IRQ_PCI1, 2);
+       ipic_set_priority(MPC83xx_IRQ_PCI2, 3);
+       ipic_set_priority(MPC83xx_IRQ_EXT0, 4);
+       ipic_set_priority(MPC83xx_IRQ_EXT1, 5);
+       ipic_set_priority(MPC83xx_IRQ_EXT2, 6);
+       ipic_set_priority(MPC83xx_IRQ_EXT3, 7);
+       ipic_set_priority(MPC83xx_IRQ_RTC_ALR, 0);
+       ipic_set_priority(MPC83xx_IRQ_MU, 1);
+       ipic_set_priority(MPC83xx_IRQ_SBA, 2);
+       ipic_set_priority(MPC83xx_IRQ_DMA, 3);
+       ipic_set_priority(MPC83xx_IRQ_EXT4, 4);
+       ipic_set_priority(MPC83xx_IRQ_EXT5, 5);
+       ipic_set_priority(MPC83xx_IRQ_EXT6, 6);
+       ipic_set_priority(MPC83xx_IRQ_EXT7, 7);
+}
+
+void ipic_enable_mcp(enum ipic_mcp_irq mcp_irq)
+{
+       struct ipic *ipic = primary_ipic;
+       u32 temp;
+
+       temp = ipic_read(ipic->regs, IPIC_SERMR);
+       temp |= (1 << (31 - mcp_irq));
+       ipic_write(ipic->regs, IPIC_SERMR, temp);
+}
+
+void ipic_disable_mcp(enum ipic_mcp_irq mcp_irq)
+{
+       struct ipic *ipic = primary_ipic;
+       u32 temp;
+
+       temp = ipic_read(ipic->regs, IPIC_SERMR);
+       temp &= (1 << (31 - mcp_irq));
+       ipic_write(ipic->regs, IPIC_SERMR, temp);
+}
+
+u32 ipic_get_mcp_status(void)
+{
+       return ipic_read(primary_ipic->regs, IPIC_SERMR);
+}
+
+void ipic_clear_mcp_status(u32 mask)
+{
+       ipic_write(primary_ipic->regs, IPIC_SERMR, mask);
+}
+
+/* Return an interrupt vector or -1 if no interrupt is pending. */
+int ipic_get_irq(struct pt_regs *regs)
+{
+       int irq;
+
+       irq = ipic_read(primary_ipic->regs, IPIC_SIVCR) & 0x7f;
+
+       if (irq == 0)    /* 0 --> no irq is pending */
+               irq = -1;
+
+       return irq;
+}
+
+static struct sysdev_class ipic_sysclass = {
+       set_kset_name("ipic"),
+};
+
+static struct sys_device device_ipic = {
+       .id             = 0,
+       .cls            = &ipic_sysclass,
+};
+
+static int __init init_ipic_sysfs(void)
+{
+       int rc;
+
+       if (!primary_ipic->regs)
+               return -ENODEV;
+       printk(KERN_DEBUG "Registering ipic with sysfs...\n");
+
+       rc = sysdev_class_register(&ipic_sysclass);
+       if (rc) {
+               printk(KERN_ERR "Failed registering ipic sys class\n");
+               return -ENODEV;
+       }
+       rc = sysdev_register(&device_ipic);
+       if (rc) {
+               printk(KERN_ERR "Failed registering ipic sys device\n");
+               return -ENODEV;
+       }
+       return 0;
+}
+
+subsys_initcall(init_ipic_sysfs);
diff --git a/arch/ppc/syslib/ipic.h b/arch/ppc/syslib/ipic.h
new file mode 100644 (file)
index 0000000..2b56a4f
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * arch/ppc/kernel/ipic.h
+ *
+ * IPIC private definitions and structure.
+ *
+ * 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.
+ */
+#ifndef __IPIC_H__
+#define __IPIC_H__
+
+#include <asm/ipic.h>
+
+#define MPC83xx_IPIC_SIZE      (0x00100)
+
+/* System Global Interrupt Configuration Register */
+#define        SICFR_IPSA      0x00010000
+#define        SICFR_IPSD      0x00080000
+#define        SICFR_MPSA      0x00200000
+#define        SICFR_MPSB      0x00400000
+
+/* System External Interrupt Mask Register */
+#define        SEMSR_SIRQ0     0x00008000
+
+/* System Error Control Register */
+#define SERCR_MCPR     0x00000001
+
+struct ipic {
+       volatile u32 __iomem    *regs;
+       unsigned int            irq_offset;
+};
+
+struct ipic_info {
+       u8      pend;           /* pending register offset from base */
+       u8      mask;           /* mask register offset from base */
+       u8      prio;           /* priority register offset from base */
+       u8      force;          /* force register offset from base */
+       u8      bit;            /* register bit position (as per doc)
+                                  bit mask = 1 << (31 - bit) */
+       u8      prio_mask;      /* priority mask value */
+};
+
+#endif /* __IPIC_H__ */
diff --git a/arch/ppc/syslib/m82xx_pci.c b/arch/ppc/syslib/m82xx_pci.c
new file mode 100644 (file)
index 0000000..5e7a7ed
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ *
+ * (C) Copyright 2003
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2004 Red Hat, Inc.
+ *
+ * 2005 (c) MontaVista Software, Inc.
+ * Vitaly Bordug <vbordug@ru.mvista.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the 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/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.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/immap_cpm2.h>
+#include <asm/mpc8260.h>
+#include <asm/cpm2.h>
+
+#include "m82xx_pci.h"
+
+/*
+ * Interrupt routing
+ */
+
+static inline int
+pq2pci_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 }, /* IDSEL 22 - PCI slot 0 */
+               { PIRQD, PIRQA, PIRQB, PIRQC }, /* IDSEL 23 - PCI slot 1 */
+               { PIRQC, PIRQD, PIRQA, PIRQB }, /* IDSEL 24 - PCI slot 2 */
+       };
+
+       const long min_idsel = 22, max_idsel = 24, irqs_per_slot = 4;
+       return PCI_IRQ_TABLE_LOOKUP;
+}
+
+static void
+pq2pci_mask_irq(unsigned int irq)
+{
+       int bit = irq - NR_CPM_INTS;
+
+       *(volatile unsigned long *) PCI_INT_MASK_REG |= (1 << (31 - bit));
+       return;
+}
+
+static void
+pq2pci_unmask_irq(unsigned int irq)
+{
+       int bit = irq - NR_CPM_INTS;
+
+       *(volatile unsigned long *) PCI_INT_MASK_REG &= ~(1 << (31 - bit));
+       return;
+}
+
+static void
+pq2pci_mask_and_ack(unsigned int irq)
+{
+       int bit = irq - NR_CPM_INTS;
+
+       *(volatile unsigned long *) PCI_INT_MASK_REG |= (1 << (31 - bit));
+       return;
+}
+
+static void
+pq2pci_end_irq(unsigned int irq)
+{
+       int bit = irq - NR_CPM_INTS;
+
+       *(volatile unsigned long *) PCI_INT_MASK_REG &= ~(1 << (31 - bit));
+       return;
+}
+
+struct hw_interrupt_type pq2pci_ic = {
+       "PQ2 PCI",
+       NULL,
+       NULL,
+       pq2pci_unmask_irq,
+       pq2pci_mask_irq,
+       pq2pci_mask_and_ack,
+       pq2pci_end_irq,
+       0
+};
+
+static irqreturn_t
+pq2pci_irq_demux(int irq, void *dev_id, struct pt_regs *regs)
+{
+       unsigned long stat, mask, pend;
+       int bit;
+
+       for(;;) {
+               stat = *(volatile unsigned long *) PCI_INT_STAT_REG;
+               mask = *(volatile unsigned long *) PCI_INT_MASK_REG;
+               pend = stat & ~mask & 0xf0000000;
+               if (!pend)
+                       break;
+               for (bit = 0; pend != 0; ++bit, pend <<= 1) {
+                       if (pend & 0x80000000)
+                               __do_IRQ(NR_CPM_INTS + bit, regs);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+static struct irqaction pq2pci_irqaction = {
+       .handler = pq2pci_irq_demux,
+       .flags   = SA_INTERRUPT,
+       .mask    = CPU_MASK_NONE,
+       .name    = "PQ2 PCI cascade",
+};
+
+
+void
+pq2pci_init_irq(void)
+{
+       int irq;
+       volatile cpm2_map_t *immap = cpm2_immr;
+#if defined CONFIG_ADS8272
+       /* configure chip select for PCI interrupt controller */
+       immap->im_memctl.memc_br3 = PCI_INT_STAT_REG | 0x00001801;
+       immap->im_memctl.memc_or3 = 0xffff8010;
+#elif defined CONFIG_PQ2FADS
+       immap->im_memctl.memc_br8 = PCI_INT_STAT_REG | 0x00001801;
+       immap->im_memctl.memc_or8 = 0xffff8010;
+#endif
+       for (irq = NR_CPM_INTS; irq < NR_CPM_INTS + 4; irq++)
+               irq_desc[irq].handler = &pq2pci_ic;
+
+       /* make PCI IRQ level sensitive */
+       immap->im_intctl.ic_siexr &=
+               ~(1 << (14 - (PCI_INT_TO_SIU - SIU_INT_IRQ1)));
+
+       /* mask all PCI interrupts */
+       *(volatile unsigned long *) PCI_INT_MASK_REG |= 0xfff00000;
+
+       /* install the demultiplexer for the PCI cascade interrupt */
+       setup_irq(PCI_INT_TO_SIU, &pq2pci_irqaction);
+       return;
+}
+
+static int
+pq2pci_exclude_device(u_char bus, u_char devfn)
+{
+       return PCIBIOS_SUCCESSFUL;
+}
+
+/* PCI bus configuration registers.
+ */
+static void
+pq2ads_setup_pci(struct pci_controller *hose)
+{
+       __u32 val;
+       volatile cpm2_map_t *immap = cpm2_immr;
+       bd_t* binfo = (bd_t*) __res;
+       u32 sccr = immap->im_clkrst.car_sccr;
+       uint pci_div,freq,time;
+               /* PCI int lowest prio */
+       /* Each 4 bits is a device bus request  and the MS 4bits
+        is highest priority */
+       /* Bus                4bit value
+          ---                ----------
+          CPM high             0b0000
+          CPM middle           0b0001
+          CPM low              0b0010
+          PCI reguest          0b0011
+          Reserved             0b0100
+          Reserved             0b0101
+          Internal Core        0b0110
+          External Master 1    0b0111
+          External Master 2    0b1000
+          External Master 3    0b1001
+          The rest are reserved
+        */
+       immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x61207893;
+       /* park bus on core */
+       immap->im_siu_conf.siu_82xx.sc_ppc_acr = PPC_ACR_BUS_PARK_CORE;
+       /*
+        * Set up master windows that allow the CPU to access PCI space. These
+        * windows are set up using the two SIU PCIBR registers.
+        */
+
+       immap->im_memctl.memc_pcimsk0 = M82xx_PCI_PRIM_WND_SIZE;
+       immap->im_memctl.memc_pcibr0  = M82xx_PCI_PRIM_WND_BASE | PCIBR_ENABLE;
+
+#ifdef M82xx_PCI_SEC_WND_SIZE
+       immap->im_memctl.memc_pcimsk1 = M82xx_PCI_SEC_WND_SIZE;
+       immap->im_memctl.memc_pcibr1  = M82xx_PCI_SEC_WND_BASE | PCIBR_ENABLE;
+#endif
+
+#if defined CONFIG_ADS8272
+       immap->im_siu_conf.siu_82xx.sc_siumcr =
+               (immap->im_siu_conf.siu_82xx.sc_siumcr &
+               ~(SIUMCR_BBD | SIUMCR_ESE | SIUMCR_PBSE |
+               SIUMCR_CDIS | SIUMCR_DPPC11 | SIUMCR_L2CPC11 |
+               SIUMCR_LBPC11 | SIUMCR_APPC11 |
+               SIUMCR_CS10PC11 | SIUMCR_BCTLC11 | SIUMCR_MMR11)) |
+               SIUMCR_DPPC11 | SIUMCR_L2CPC01 | SIUMCR_LBPC00 |
+               SIUMCR_APPC10 | SIUMCR_CS10PC00 |
+               SIUMCR_BCTLC00 | SIUMCR_MMR11 ;
+
+#elif defined CONFIG_PQ2FADS
+       /*
+        * Setting required to enable IRQ1-IRQ7 (SIUMCR [DPPC]),
+        * and local bus for PCI (SIUMCR [LBPC]).
+        */
+       immap->im_siu_conf.siu_82xx.sc_siumcr = (immap->im_siu_conf.sc_siumcr &
+                               ~(SIUMCR_L2PC11 | SIUMCR_LBPC11 | SIUMCR_CS10PC11 | SIUMCR_APPC11) |
+                               SIUMCR_BBD | SIUMCR_LBPC01 | SIUMCR_DPPC11 | SIUMCR_APPC10;
+#endif
+       /* Enable PCI  */
+       immap->im_pci.pci_gcr = cpu_to_le32(PCIGCR_PCI_BUS_EN);
+
+       pci_div = ( (sccr & SCCR_PCI_MODCK) ? 2 : 1) *
+                       ( ( (sccr & SCCR_PCIDF_MSK) >> SCCR_PCIDF_SHIFT) + 1);
+       freq = (uint)((2*binfo->bi_cpmfreq)/(pci_div));
+       time = (int)666666/freq;
+       /* due to PCI Local Bus spec, some devices needs to wait such a long
+       time after RST  deassertion. More specifically, 0.508s for 66MHz & twice more for 33 */
+       printk("%s: The PCI bus is %d Mhz.\nWaiting %s after deasserting RST...\n",__FILE__,freq,
+       (time==1) ? "0.5 seconds":"1 second" );
+
+       {
+               int i;
+               for(i=0;i<(500*time);i++)
+                       udelay(1000);
+       }
+
+       /* setup ATU registers */
+       immap->im_pci.pci_pocmr0 = cpu_to_le32(POCMR_ENABLE | POCMR_PCI_IO |
+                               ((~(M82xx_PCI_IO_SIZE - 1U)) >> POTA_ADDR_SHIFT));
+       immap->im_pci.pci_potar0 = cpu_to_le32(M82xx_PCI_LOWER_IO >> POTA_ADDR_SHIFT);
+       immap->im_pci.pci_pobar0 = cpu_to_le32(M82xx_PCI_IO_BASE >> POTA_ADDR_SHIFT);
+
+       /* Set-up non-prefetchable window */
+       immap->im_pci.pci_pocmr1 = cpu_to_le32(POCMR_ENABLE | ((~(M82xx_PCI_MMIO_SIZE-1U)) >> POTA_ADDR_SHIFT));
+       immap->im_pci.pci_potar1 = cpu_to_le32(M82xx_PCI_LOWER_MMIO >> POTA_ADDR_SHIFT);
+       immap->im_pci.pci_pobar1 = cpu_to_le32((M82xx_PCI_LOWER_MMIO - M82xx_PCI_MMIO_OFFSET) >> POTA_ADDR_SHIFT);
+
+       /* Set-up prefetchable window */
+       immap->im_pci.pci_pocmr2 = cpu_to_le32(POCMR_ENABLE |POCMR_PREFETCH_EN |
+               (~(M82xx_PCI_MEM_SIZE-1U) >> POTA_ADDR_SHIFT));
+       immap->im_pci.pci_potar2 = cpu_to_le32(M82xx_PCI_LOWER_MEM >> POTA_ADDR_SHIFT);
+       immap->im_pci.pci_pobar2 = cpu_to_le32((M82xx_PCI_LOWER_MEM - M82xx_PCI_MEM_OFFSET) >> POTA_ADDR_SHIFT);
+
+       /* Inbound transactions from PCI memory space */
+       immap->im_pci.pci_picmr0 = cpu_to_le32(PICMR_ENABLE | PICMR_PREFETCH_EN |
+                                       ((~(M82xx_PCI_SLAVE_MEM_SIZE-1U)) >> PITA_ADDR_SHIFT));
+       immap->im_pci.pci_pibar0 = cpu_to_le32(M82xx_PCI_SLAVE_MEM_BUS  >> PITA_ADDR_SHIFT);
+       immap->im_pci.pci_pitar0 = cpu_to_le32(M82xx_PCI_SLAVE_MEM_LOCAL>> PITA_ADDR_SHIFT);
+
+#if defined CONFIG_ADS8272
+       /* PCI int highest prio */
+       immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x01236745;
+#elif defined CONFIG_PQ2FADS
+       immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x03124567;
+#endif
+       /* park bus on PCI */
+       immap->im_siu_conf.siu_82xx.sc_ppc_acr = PPC_ACR_BUS_PARK_PCI;
+
+       /* Enable bus mastering and inbound memory transactions */
+       early_read_config_dword(hose, hose->first_busno, 0, PCI_COMMAND, &val);
+       val &= 0xffff0000;
+       val |= PCI_COMMAND_MEMORY|PCI_COMMAND_MASTER;
+       early_write_config_dword(hose, hose->first_busno, 0, PCI_COMMAND, val);
+
+}
+
+void __init pq2_find_bridges(void)
+{
+       extern int pci_assign_all_busses;
+       struct pci_controller * hose;
+       int host_bridge;
+
+       pci_assign_all_busses = 1;
+
+       hose = pcibios_alloc_controller();
+
+       if (!hose)
+               return;
+
+       ppc_md.pci_swizzle = common_swizzle;
+
+       hose->first_busno = 0;
+       hose->bus_offset = 0;
+       hose->last_busno = 0xff;
+
+#ifdef CONFIG_ADS8272
+       hose->set_cfg_type = 1;
+#endif
+
+       setup_m8260_indirect_pci(hose,
+                                (unsigned long)&cpm2_immr->im_pci.pci_cfg_addr,
+                                (unsigned long)&cpm2_immr->im_pci.pci_cfg_data);
+
+       /* Make sure it is a supported bridge */
+       early_read_config_dword(hose,
+                               0,
+                               PCI_DEVFN(0,0),
+                               PCI_VENDOR_ID,
+                               &host_bridge);
+       switch (host_bridge) {
+               case PCI_DEVICE_ID_MPC8265:
+                       break;
+               case PCI_DEVICE_ID_MPC8272:
+                       break;
+               default:
+                       printk("Attempting to use unrecognized host bridge ID"
+                               " 0x%08x.\n", host_bridge);
+                       break;
+       }
+
+       pq2ads_setup_pci(hose);
+
+       hose->io_space.start =  M82xx_PCI_LOWER_IO;
+       hose->io_space.end = M82xx_PCI_UPPER_IO;
+       hose->mem_space.start = M82xx_PCI_LOWER_MEM;
+       hose->mem_space.end = M82xx_PCI_UPPER_MMIO;
+       hose->pci_mem_offset = M82xx_PCI_MEM_OFFSET;
+
+       isa_io_base =
+       (unsigned long) ioremap(M82xx_PCI_IO_BASE,
+                                       M82xx_PCI_IO_SIZE);
+       hose->io_base_virt = (void *) isa_io_base;
+
+       /* setup resources */
+       pci_init_resource(&hose->mem_resources[0],
+                       M82xx_PCI_LOWER_MEM,
+                       M82xx_PCI_UPPER_MEM,
+                       IORESOURCE_MEM|IORESOURCE_PREFETCH, "PCI prefetchable memory");
+
+       pci_init_resource(&hose->mem_resources[1],
+                       M82xx_PCI_LOWER_MMIO,
+                       M82xx_PCI_UPPER_MMIO,
+                       IORESOURCE_MEM, "PCI memory");
+
+       pci_init_resource(&hose->io_resource,
+                       M82xx_PCI_LOWER_IO,
+                       M82xx_PCI_UPPER_IO,
+                       IORESOURCE_IO | 1, "PCI I/O");
+
+       ppc_md.pci_exclude_device = pq2pci_exclude_device;
+       hose->last_busno = pciauto_bus_scan(hose, hose->first_busno);
+
+       ppc_md.pci_map_irq = pq2pci_map_irq;
+       ppc_md.pcibios_fixup = NULL;
+       ppc_md.pcibios_fixup_bus = NULL;
+
+}
diff --git a/arch/ppc/syslib/mpc52xx_devices.c b/arch/ppc/syslib/mpc52xx_devices.c
new file mode 100644 (file)
index 0000000..ad5182e
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * arch/ppc/syslib/mpc52xx_devices.c
+ *
+ * Freescale MPC52xx device descriptions
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Copyright (C) 2005 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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/fsl_devices.h>
+#include <linux/resource.h>
+#include <asm/mpc52xx.h>
+#include <asm/ppc_sys.h>
+
+
+static u64 mpc52xx_dma_mask = 0xffffffffULL;
+
+static struct fsl_i2c_platform_data mpc52xx_fsl_i2c_pdata = {
+       .device_flags = FSL_I2C_DEV_CLOCK_5200,
+};
+
+
+/* We use relative offsets for IORESOURCE_MEM to be independent from the
+ * MBAR location at compile time
+ */
+
+/* TODO Add the BestComm initiator channel to the device definitions,
+   possibly using IORESOURCE_DMA. But that's when BestComm is ready ... */
+
+struct platform_device ppc_sys_platform_devices[] = {
+       [MPC52xx_MSCAN1] = {
+               .name           = "mpc52xx-mscan",
+               .id             = 0,
+               .num_resources  = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x0900,
+                               .end    = 0x097f,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC52xx_MSCAN1_IRQ,
+                               .end    = MPC52xx_MSCAN1_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC52xx_MSCAN2] = {
+               .name           = "mpc52xx-mscan",
+               .id             = 1,
+               .num_resources  = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x0980,
+                               .end    = 0x09ff,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC52xx_MSCAN2_IRQ,
+                               .end    = MPC52xx_MSCAN2_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC52xx_SPI] = {
+               .name           = "mpc52xx-spi",
+               .id             = -1,
+               .num_resources  = 3,
+               .resource       = (struct resource[]) {
+                       {
+                               .start  = 0x0f00,
+                               .end    = 0x0f1f,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .name   = "modf",
+                               .start  = MPC52xx_SPI_MODF_IRQ,
+                               .end    = MPC52xx_SPI_MODF_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+                       {
+                               .name   = "spif",
+                               .start  = MPC52xx_SPI_SPIF_IRQ,
+                               .end    = MPC52xx_SPI_SPIF_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC52xx_USB] = {
+               .name           = "ppc-soc-ohci",
+               .id             = -1,
+               .num_resources  = 2,
+               .dev.dma_mask   = &mpc52xx_dma_mask,
+               .dev.coherent_dma_mask = 0xffffffffULL,
+               .resource       = (struct resource[]) {
+                       {
+                               .start  = 0x1000,
+                               .end    = 0x10ff,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC52xx_USB_IRQ,
+                               .end    = MPC52xx_USB_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC52xx_BDLC] = {
+               .name           = "mpc52xx-bdlc",
+               .id             = -1,
+               .num_resources  = 2,
+               .resource       = (struct resource[]) {
+                       {
+                               .start  = 0x1300,
+                               .end    = 0x130f,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC52xx_BDLC_IRQ,
+                               .end    = MPC52xx_BDLC_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC52xx_PSC1] = {
+               .name           = "mpc52xx-psc",
+               .id             = 0,
+               .num_resources  = 2,
+               .resource       = (struct resource[]) {
+                       {
+                               .start  = 0x2000,
+                               .end    = 0x209f,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC52xx_PSC1_IRQ,
+                               .end    = MPC52xx_PSC1_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC52xx_PSC2] = {
+               .name           = "mpc52xx-psc",
+               .id             = 1,
+               .num_resources  = 2,
+               .resource       = (struct resource[]) {
+                       {
+                               .start  = 0x2200,
+                               .end    = 0x229f,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC52xx_PSC2_IRQ,
+                               .end    = MPC52xx_PSC2_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC52xx_PSC3] = {
+               .name           = "mpc52xx-psc",
+               .id             = 2,
+               .num_resources  = 2,
+               .resource       = (struct resource[]) {
+                       {
+                               .start  = 0x2400,
+                               .end    = 0x249f,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC52xx_PSC3_IRQ,
+                               .end    = MPC52xx_PSC3_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC52xx_PSC4] = {
+               .name           = "mpc52xx-psc",
+               .id             = 3,
+               .num_resources  = 2,
+               .resource       = (struct resource[]) {
+                       {
+                               .start  = 0x2600,
+                               .end    = 0x269f,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC52xx_PSC4_IRQ,
+                               .end    = MPC52xx_PSC4_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC52xx_PSC5] = {
+               .name           = "mpc52xx-psc",
+               .id             = 4,
+               .num_resources  = 2,
+               .resource       = (struct resource[]) {
+                       {
+                               .start  = 0x2800,
+                               .end    = 0x289f,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC52xx_PSC5_IRQ,
+                               .end    = MPC52xx_PSC5_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC52xx_PSC6] = {
+               .name           = "mpc52xx-psc",
+               .id             = 5,
+               .num_resources  = 2,
+               .resource       = (struct resource[]) {
+                       {
+                               .start  = 0x2c00,
+                               .end    = 0x2c9f,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC52xx_PSC6_IRQ,
+                               .end    = MPC52xx_PSC6_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC52xx_FEC] = {
+               .name           = "mpc52xx-fec",
+               .id             = -1,
+               .num_resources  = 2,
+               .resource       = (struct resource[]) {
+                       {
+                               .start  = 0x3000,
+                               .end    = 0x33ff,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC52xx_FEC_IRQ,
+                               .end    = MPC52xx_FEC_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC52xx_ATA] = {
+               .name           = "mpc52xx-ata",
+               .id             = -1,
+               .num_resources  = 2,
+               .resource       = (struct resource[]) {
+                       {
+                               .start  = 0x3a00,
+                               .end    = 0x3aff,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC52xx_ATA_IRQ,
+                               .end    = MPC52xx_ATA_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC52xx_I2C1] = {
+               .name           = "fsl-i2c",
+               .id             = 0,
+               .dev.platform_data = &mpc52xx_fsl_i2c_pdata,
+               .num_resources  = 2,
+               .resource       = (struct resource[]) {
+                       {
+                               .start  = 0x3d00,
+                               .end    = 0x3d1f,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC52xx_I2C1_IRQ,
+                               .end    = MPC52xx_I2C1_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC52xx_I2C2] = {
+               .name           = "fsl-i2c",
+               .id             = 1,
+               .dev.platform_data = &mpc52xx_fsl_i2c_pdata,
+               .num_resources  = 2,
+               .resource       = (struct resource[]) {
+                       {
+                               .start  = 0x3d40,
+                               .end    = 0x3d5f,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC52xx_I2C2_IRQ,
+                               .end    = MPC52xx_I2C2_IRQ,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+};
+
+
+static int __init mach_mpc52xx_fixup(struct platform_device *pdev)
+{
+       ppc_sys_fixup_mem_resource(pdev, MPC52xx_MBAR);
+       return 0;
+}
+
+static int __init mach_mpc52xx_init(void)
+{
+       ppc_sys_device_fixup = mach_mpc52xx_fixup;
+       return 0;
+}
+
+postcore_initcall(mach_mpc52xx_init);
diff --git a/arch/ppc/syslib/mpc52xx_pci.c b/arch/ppc/syslib/mpc52xx_pci.c
new file mode 100644 (file)
index 0000000..59cf3e8
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * arch/ppc/syslib/mpc52xx_pci.c
+ *
+ * PCI code for the Freescale MPC52xx embedded CPU.
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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 <asm/pci.h>
+
+#include <asm/mpc52xx.h>
+#include "mpc52xx_pci.h"
+
+#include <asm/delay.h>
+
+
+static int
+mpc52xx_pci_read_config(struct pci_bus *bus, unsigned int devfn,
+                               int offset, int len, u32 *val)
+{
+       struct pci_controller *hose = bus->sysdata;
+       u32 value;
+
+       if (ppc_md.pci_exclude_device)
+               if (ppc_md.pci_exclude_device(bus->number, devfn))
+                       return PCIBIOS_DEVICE_NOT_FOUND;
+
+       out_be32(hose->cfg_addr,
+               (1 << 31) |
+               ((bus->number - hose->bus_offset) << 16) |
+               (devfn << 8) |
+               (offset & 0xfc));
+
+       value = in_le32(hose->cfg_data);
+
+       if (len != 4) {
+               value >>= ((offset & 0x3) << 3);
+               value &= 0xffffffff >> (32 - (len << 3));
+       }
+
+       *val = value;
+
+       out_be32(hose->cfg_addr, 0);
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+mpc52xx_pci_write_config(struct pci_bus *bus, unsigned int devfn,
+                               int offset, int len, u32 val)
+{
+       struct pci_controller *hose = bus->sysdata;
+       u32 value, mask;
+
+       if (ppc_md.pci_exclude_device)
+               if (ppc_md.pci_exclude_device(bus->number, devfn))
+                       return PCIBIOS_DEVICE_NOT_FOUND;
+
+       out_be32(hose->cfg_addr,
+               (1 << 31) |
+               ((bus->number - hose->bus_offset) << 16) |
+               (devfn << 8) |
+               (offset & 0xfc));
+
+       if (len != 4) {
+               value = in_le32(hose->cfg_data);
+
+               offset = (offset & 0x3) << 3;
+               mask = (0xffffffff >> (32 - (len << 3)));
+               mask <<= offset;
+
+               value &= ~mask;
+               val = value | ((val << offset) & mask);
+       }
+
+       out_le32(hose->cfg_data, val);
+
+       out_be32(hose->cfg_addr, 0);
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops mpc52xx_pci_ops = {
+       .read  = mpc52xx_pci_read_config,
+       .write = mpc52xx_pci_write_config
+};
+
+
+static void __init
+mpc52xx_pci_setup(struct mpc52xx_pci __iomem *pci_regs)
+{
+
+       /* Setup control regs */
+               /* Nothing to do afaik */
+
+       /* Setup windows */
+       out_be32(&pci_regs->iw0btar, MPC52xx_PCI_IWBTAR_TRANSLATION(
+               MPC52xx_PCI_MEM_START + MPC52xx_PCI_MEM_OFFSET,
+               MPC52xx_PCI_MEM_START,
+               MPC52xx_PCI_MEM_SIZE ));
+
+       out_be32(&pci_regs->iw1btar, MPC52xx_PCI_IWBTAR_TRANSLATION(
+               MPC52xx_PCI_MMIO_START + MPC52xx_PCI_MEM_OFFSET,
+               MPC52xx_PCI_MMIO_START,
+               MPC52xx_PCI_MMIO_SIZE ));
+
+       out_be32(&pci_regs->iw2btar, MPC52xx_PCI_IWBTAR_TRANSLATION(
+               MPC52xx_PCI_IO_BASE,
+               MPC52xx_PCI_IO_START,
+               MPC52xx_PCI_IO_SIZE ));
+
+       out_be32(&pci_regs->iwcr, MPC52xx_PCI_IWCR_PACK(
+               ( MPC52xx_PCI_IWCR_ENABLE |             /* iw0btar */
+                 MPC52xx_PCI_IWCR_READ_MULTI |
+                 MPC52xx_PCI_IWCR_MEM ),
+               ( MPC52xx_PCI_IWCR_ENABLE |             /* iw1btar */
+                 MPC52xx_PCI_IWCR_READ |
+                 MPC52xx_PCI_IWCR_MEM ),
+               ( MPC52xx_PCI_IWCR_ENABLE |             /* iw2btar */
+                 MPC52xx_PCI_IWCR_IO )
+       ));
+
+
+       out_be32(&pci_regs->tbatr0,
+               MPC52xx_PCI_TBATR_ENABLE | MPC52xx_PCI_TARGET_IO );
+       out_be32(&pci_regs->tbatr1,
+               MPC52xx_PCI_TBATR_ENABLE | MPC52xx_PCI_TARGET_MEM );
+
+       out_be32(&pci_regs->tcr, MPC52xx_PCI_TCR_LD);
+
+       /* Reset the exteral bus ( internal PCI controller is NOT resetted ) */
+       /* Not necessary and can be a bad thing if for example the bootloader
+          is displaying a splash screen or ... Just left here for
+          documentation purpose if anyone need it */
+#if 0
+       u32 tmp;
+       tmp = in_be32(&pci_regs->gscr);
+       out_be32(&pci_regs->gscr, tmp | MPC52xx_PCI_GSCR_PR);
+       udelay(50);
+       out_be32(&pci_regs->gscr, tmp);
+#endif
+}
+
+static void __init
+mpc52xx_pci_fixup_resources(struct pci_dev *dev)
+{
+       int i;
+
+       /* We don't rely on boot loader for PCI and resets all
+          devices */
+       for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+               struct resource *res = &dev->resource[i];
+               if (res->end > res->start) {    /* Only valid resources */
+                       res->end -= res->start;
+                       res->start = 0;
+                       res->flags |= IORESOURCE_UNSET;
+               }
+       }
+
+       /* The PCI Host bridge of MPC52xx has a prefetch memory resource
+          fixed to 1Gb. Doesn't fit in the resource system so we remove it */
+       if ( (dev->vendor == PCI_VENDOR_ID_MOTOROLA) &&
+            (dev->device == PCI_DEVICE_ID_MOTOROLA_MPC5200) ) {
+               struct resource *res = &dev->resource[1];
+               res->start = res->end = res->flags = 0;
+       }
+}
+
+void __init
+mpc52xx_find_bridges(void)
+{
+       struct mpc52xx_pci __iomem *pci_regs;
+       struct pci_controller *hose;
+
+       pci_assign_all_busses = 1;
+
+       pci_regs = ioremap(MPC52xx_PA(MPC52xx_PCI_OFFSET), MPC52xx_PCI_SIZE);
+       if (!pci_regs)
+               return;
+
+       hose = pcibios_alloc_controller();
+       if (!hose) {
+               iounmap(pci_regs);
+               return;
+       }
+
+       ppc_md.pci_swizzle = common_swizzle;
+       ppc_md.pcibios_fixup_resources = mpc52xx_pci_fixup_resources;
+
+       hose->first_busno = 0;
+       hose->last_busno = 0xff;
+       hose->bus_offset = 0;
+       hose->ops = &mpc52xx_pci_ops;
+
+       mpc52xx_pci_setup(pci_regs);
+
+       hose->pci_mem_offset = MPC52xx_PCI_MEM_OFFSET;
+
+       hose->io_base_virt = ioremap(MPC52xx_PCI_IO_BASE, MPC52xx_PCI_IO_SIZE);
+       isa_io_base = (unsigned long) hose->io_base_virt;
+
+       hose->cfg_addr = &pci_regs->car;
+       hose->cfg_data = hose->io_base_virt;
+
+       /* Setup resources */
+       pci_init_resource(&hose->mem_resources[0],
+                       MPC52xx_PCI_MEM_START,
+                       MPC52xx_PCI_MEM_STOP,
+                       IORESOURCE_MEM|IORESOURCE_PREFETCH,
+                       "PCI prefetchable memory");
+
+       pci_init_resource(&hose->mem_resources[1],
+                       MPC52xx_PCI_MMIO_START,
+                       MPC52xx_PCI_MMIO_STOP,
+                       IORESOURCE_MEM,
+                       "PCI memory");
+
+       pci_init_resource(&hose->io_resource,
+                       MPC52xx_PCI_IO_START,
+                       MPC52xx_PCI_IO_STOP,
+                       IORESOURCE_IO,
+                       "PCI I/O");
+
+}
diff --git a/arch/ppc/syslib/mpc52xx_pci.h b/arch/ppc/syslib/mpc52xx_pci.h
new file mode 100644 (file)
index 0000000..04b509a
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * arch/ppc/syslib/mpc52xx_pci.h
+ *
+ * PCI Include file the Freescale MPC52xx embedded cpu chips
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Inspired from code written by Dale Farnsworth <dfarnsworth@mvista.com>
+ * for the 2.4 kernel.
+ *
+ * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003 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 __SYSLIB_MPC52xx_PCI_H__
+#define __SYSLIB_MPC52xx_PCI_H__
+
+/* ======================================================================== */
+/* PCI windows config                                                       */
+/* ======================================================================== */
+
+/*
+ * Master windows : MPC52xx -> PCI
+ *
+ *  0x80000000 -> 0x9FFFFFFF       PCI Mem prefetchable          IW0BTAR
+ *  0xA0000000 -> 0xAFFFFFFF       PCI Mem                       IW1BTAR
+ *  0xB0000000 -> 0xB0FFFFFF       PCI IO                        IW2BTAR
+ *
+ * Slave windows  : PCI -> MPC52xx
+ *
+ *  0xF0000000 -> 0xF003FFFF       MPC52xx MBAR                  TBATR0
+ *  0x00000000 -> 0x3FFFFFFF       MPC52xx local memory          TBATR1
+ */
+
+#define MPC52xx_PCI_MEM_OFFSET         0x00000000      /* Offset for MEM MMIO */
+
+#define MPC52xx_PCI_MEM_START  0x80000000
+#define MPC52xx_PCI_MEM_SIZE   0x20000000
+#define MPC52xx_PCI_MEM_STOP   (MPC52xx_PCI_MEM_START+MPC52xx_PCI_MEM_SIZE-1)
+
+#define MPC52xx_PCI_MMIO_START 0xa0000000
+#define MPC52xx_PCI_MMIO_SIZE  0x10000000
+#define MPC52xx_PCI_MMIO_STOP  (MPC52xx_PCI_MMIO_START+MPC52xx_PCI_MMIO_SIZE-1)
+
+#define MPC52xx_PCI_IO_BASE    0xb0000000
+
+#define MPC52xx_PCI_IO_START   0x00000000
+#define MPC52xx_PCI_IO_SIZE    0x01000000
+#define MPC52xx_PCI_IO_STOP    (MPC52xx_PCI_IO_START+MPC52xx_PCI_IO_SIZE-1)
+
+
+#define MPC52xx_PCI_TARGET_IO  MPC52xx_MBAR
+#define MPC52xx_PCI_TARGET_MEM 0x00000000
+
+
+/* ======================================================================== */
+/* Structures mapping & Defines for PCI Unit                                */
+/* ======================================================================== */
+
+#define MPC52xx_PCI_GSCR_BM            0x40000000
+#define MPC52xx_PCI_GSCR_PE            0x20000000
+#define MPC52xx_PCI_GSCR_SE            0x10000000
+#define MPC52xx_PCI_GSCR_XLB2PCI_MASK  0x07000000
+#define MPC52xx_PCI_GSCR_XLB2PCI_SHIFT 24
+#define MPC52xx_PCI_GSCR_IPG2PCI_MASK  0x00070000
+#define MPC52xx_PCI_GSCR_IPG2PCI_SHIFT 16
+#define MPC52xx_PCI_GSCR_BME           0x00004000
+#define MPC52xx_PCI_GSCR_PEE           0x00002000
+#define MPC52xx_PCI_GSCR_SEE           0x00001000
+#define MPC52xx_PCI_GSCR_PR            0x00000001
+
+
+#define MPC52xx_PCI_IWBTAR_TRANSLATION(proc_ad,pci_ad,size)      \
+               ( ( (proc_ad) & 0xff000000 )                    | \
+                 ( (((size) - 1) >> 8) & 0x00ff0000 )          | \
+                 ( ((pci_ad) >> 16) & 0x0000ff00 ) )
+
+#define MPC52xx_PCI_IWCR_PACK(win0,win1,win2)  (((win0) << 24) | \
+                                                ((win1) << 16) | \
+                                                ((win2) <<  8))
+
+#define MPC52xx_PCI_IWCR_DISABLE       0x0
+#define MPC52xx_PCI_IWCR_ENABLE                0x1
+#define MPC52xx_PCI_IWCR_READ          0x0
+#define MPC52xx_PCI_IWCR_READ_LINE     0x2
+#define MPC52xx_PCI_IWCR_READ_MULTI    0x4
+#define MPC52xx_PCI_IWCR_MEM           0x0
+#define MPC52xx_PCI_IWCR_IO            0x8
+
+#define MPC52xx_PCI_TCR_P              0x01000000
+#define MPC52xx_PCI_TCR_LD             0x00010000
+
+#define MPC52xx_PCI_TBATR_DISABLE      0x0
+#define MPC52xx_PCI_TBATR_ENABLE       0x1
+
+
+#ifndef __ASSEMBLY__
+
+struct mpc52xx_pci {
+       u32     idr;            /* PCI + 0x00 */
+       u32     scr;            /* PCI + 0x04 */
+       u32     ccrir;          /* PCI + 0x08 */
+       u32     cr1;            /* PCI + 0x0C */
+       u32     bar0;           /* PCI + 0x10 */
+       u32     bar1;           /* PCI + 0x14 */
+       u8      reserved1[16];  /* PCI + 0x18 */
+       u32     ccpr;           /* PCI + 0x28 */
+       u32     sid;            /* PCI + 0x2C */
+       u32     erbar;          /* PCI + 0x30 */
+       u32     cpr;            /* PCI + 0x34 */
+       u8      reserved2[4];   /* PCI + 0x38 */
+       u32     cr2;            /* PCI + 0x3C */
+       u8      reserved3[32];  /* PCI + 0x40 */
+       u32     gscr;           /* PCI + 0x60 */
+       u32     tbatr0;         /* PCI + 0x64 */
+       u32     tbatr1;         /* PCI + 0x68 */
+       u32     tcr;            /* PCI + 0x6C */
+       u32     iw0btar;        /* PCI + 0x70 */
+       u32     iw1btar;        /* PCI + 0x74 */
+       u32     iw2btar;        /* PCI + 0x78 */
+       u8      reserved4[4];   /* PCI + 0x7C */
+       u32     iwcr;           /* PCI + 0x80 */
+       u32     icr;            /* PCI + 0x84 */
+       u32     isr;            /* PCI + 0x88 */
+       u32     arb;            /* PCI + 0x8C */
+       u8      reserved5[104]; /* PCI + 0x90 */
+       u32     car;            /* PCI + 0xF8 */
+       u8      reserved6[4];   /* PCI + 0xFC */
+};
+
+#endif  /* __ASSEMBLY__ */
+
+
+#endif  /* __SYSLIB_MPC52xx_PCI_H__ */
diff --git a/arch/ppc/syslib/mpc52xx_sys.c b/arch/ppc/syslib/mpc52xx_sys.c
new file mode 100644 (file)
index 0000000..9a0f90a
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * arch/ppc/syslib/mpc52xx_sys.c
+ *
+ * Freescale MPC52xx system descriptions
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Copyright (C) 2005 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * 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_sys.h>
+
+struct ppc_sys_spec *cur_ppc_sys_spec;
+struct ppc_sys_spec ppc_sys_specs[] = {
+       {
+               .ppc_sys_name   = "5200",
+               .mask           = 0xffff0000,
+               .value          = 0x80110000,
+               .num_devices    = 15,
+               .device_list    = (enum ppc_sys_devices[])
+               {
+                       MPC52xx_MSCAN1, MPC52xx_MSCAN2, MPC52xx_SPI,
+                       MPC52xx_USB, MPC52xx_BDLC, MPC52xx_PSC1, MPC52xx_PSC2,
+                       MPC52xx_PSC3, MPC52xx_PSC4, MPC52xx_PSC5, MPC52xx_PSC6,
+                       MPC52xx_FEC, MPC52xx_ATA, MPC52xx_I2C1, MPC52xx_I2C2,
+               },
+       },
+       {       /* default match */
+               .ppc_sys_name   = "",
+               .mask           = 0x00000000,
+               .value          = 0x00000000,
+       },
+};
diff --git a/arch/ppc/syslib/mpc83xx_devices.c b/arch/ppc/syslib/mpc83xx_devices.c
new file mode 100644 (file)
index 0000000..75c8e98
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * arch/ppc/platforms/83xx/mpc83xx_devices.c
+ *
+ * MPC83xx 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/mpc83xx.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 IMMRBAR is, will get fixed up by mach_mpc83xx_fixup
+ */
+
+static struct gianfar_platform_data mpc83xx_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 = 0x24000,
+};
+
+static struct gianfar_platform_data mpc83xx_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 = 0x24000,
+};
+
+static struct fsl_i2c_platform_data mpc83xx_fsl_i2c1_pdata = {
+       .device_flags = FSL_I2C_DEV_SEPARATE_DFSRR,
+};
+
+static struct fsl_i2c_platform_data mpc83xx_fsl_i2c2_pdata = {
+       .device_flags = FSL_I2C_DEV_SEPARATE_DFSRR,
+};
+
+static struct plat_serial8250_port serial_platform_data[] = {
+       [0] = {
+               .mapbase        = 0x4500,
+               .irq            = MPC83xx_IRQ_UART1,
+               .iotype         = UPIO_MEM,
+               .flags          = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+       },
+       [1] = {
+               .mapbase        = 0x4600,
+               .irq            = MPC83xx_IRQ_UART2,
+               .iotype         = UPIO_MEM,
+               .flags          = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+       },
+       { },
+};
+
+struct platform_device ppc_sys_platform_devices[] = {
+       [MPC83xx_TSEC1] = {
+               .name = "fsl-gianfar",
+               .id     = 1,
+               .dev.platform_data = &mpc83xx_tsec1_pdata,
+               .num_resources   = 4,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x24000,
+                               .end    = 0x24fff,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .name   = "tx",
+                               .start  = MPC83xx_IRQ_TSEC1_TX,
+                               .end    = MPC83xx_IRQ_TSEC1_TX,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+                       {
+                               .name   = "rx",
+                               .start  = MPC83xx_IRQ_TSEC1_RX,
+                               .end    = MPC83xx_IRQ_TSEC1_RX,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+                       {
+                               .name   = "error",
+                               .start  = MPC83xx_IRQ_TSEC1_ERROR,
+                               .end    = MPC83xx_IRQ_TSEC1_ERROR,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC83xx_TSEC2] = {
+               .name = "fsl-gianfar",
+               .id     = 2,
+               .dev.platform_data = &mpc83xx_tsec2_pdata,
+               .num_resources   = 4,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x25000,
+                               .end    = 0x25fff,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .name   = "tx",
+                               .start  = MPC83xx_IRQ_TSEC2_TX,
+                               .end    = MPC83xx_IRQ_TSEC2_TX,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+                       {
+                               .name   = "rx",
+                               .start  = MPC83xx_IRQ_TSEC2_RX,
+                               .end    = MPC83xx_IRQ_TSEC2_RX,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+                       {
+                               .name   = "error",
+                               .start  = MPC83xx_IRQ_TSEC2_ERROR,
+                               .end    = MPC83xx_IRQ_TSEC2_ERROR,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC83xx_IIC1] = {
+               .name = "fsl-i2c",
+               .id     = 1,
+               .dev.platform_data = &mpc83xx_fsl_i2c1_pdata,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x3000,
+                               .end    = 0x30ff,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC83xx_IRQ_IIC1,
+                               .end    = MPC83xx_IRQ_IIC1,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC83xx_IIC2] = {
+               .name = "fsl-i2c",
+               .id     = 2,
+               .dev.platform_data = &mpc83xx_fsl_i2c2_pdata,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x3100,
+                               .end    = 0x31ff,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC83xx_IRQ_IIC2,
+                               .end    = MPC83xx_IRQ_IIC2,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC83xx_DUART] = {
+               .name = "serial8250",
+               .id     = 0,
+               .dev.platform_data = serial_platform_data,
+       },
+       [MPC83xx_SEC2] = {
+               .name = "fsl-sec2",
+               .id     = 1,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x30000,
+                               .end    = 0x3ffff,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC83xx_IRQ_SEC2,
+                               .end    = MPC83xx_IRQ_SEC2,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC83xx_USB2_DR] = {
+               .name = "fsl-usb2-dr",
+               .id     = 1,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x22000,
+                               .end    = 0x22fff,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC83xx_IRQ_USB2_DR,
+                               .end    = MPC83xx_IRQ_USB2_DR,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+       [MPC83xx_USB2_MPH] = {
+               .name = "fsl-usb2-mph",
+               .id     = 1,
+               .num_resources   = 2,
+               .resource = (struct resource[]) {
+                       {
+                               .start  = 0x23000,
+                               .end    = 0x23fff,
+                               .flags  = IORESOURCE_MEM,
+                       },
+                       {
+                               .start  = MPC83xx_IRQ_USB2_MPH,
+                               .end    = MPC83xx_IRQ_USB2_MPH,
+                               .flags  = IORESOURCE_IRQ,
+                       },
+               },
+       },
+};
+
+static int __init mach_mpc83xx_fixup(struct platform_device *pdev)
+{
+       ppc_sys_fixup_mem_resource(pdev, immrbar);
+       return 0;
+}
+
+static int __init mach_mpc83xx_init(void)
+{
+       if (ppc_md.progress)
+               ppc_md.progress("mach_mpc83xx_init:enter", 0);
+       ppc_sys_device_fixup = mach_mpc83xx_fixup;
+       return 0;
+}
+
+postcore_initcall(mach_mpc83xx_init);
diff --git a/arch/ppc/syslib/mpc83xx_sys.c b/arch/ppc/syslib/mpc83xx_sys.c
new file mode 100644 (file)
index 0000000..29aa633
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * arch/ppc/platforms/83xx/mpc83xx_sys.c
+ *
+ * MPC83xx 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   = "8349E",
+               .mask           = 0xFFFF0000,
+               .value          = 0x80500000,
+               .num_devices    = 8,
+               .device_list    = (enum ppc_sys_devices[])
+               {
+                       MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
+                       MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2,
+                       MPC83xx_USB2_DR, MPC83xx_USB2_MPH
+               },
+       },
+       {
+               .ppc_sys_name   = "8349",
+               .mask           = 0xFFFF0000,
+               .value          = 0x80510000,
+               .num_devices    = 7,
+               .device_list    = (enum ppc_sys_devices[])
+               {
+                       MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
+                       MPC83xx_IIC2, MPC83xx_DUART,
+                       MPC83xx_USB2_DR, MPC83xx_USB2_MPH
+               },
+       },
+       {
+               .ppc_sys_name   = "8347E",
+               .mask           = 0xFFFF0000,
+               .value          = 0x80520000,
+               .num_devices    = 8,
+               .device_list    = (enum ppc_sys_devices[])
+               {
+                       MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
+                       MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2,
+                       MPC83xx_USB2_DR, MPC83xx_USB2_MPH
+               },
+       },
+       {
+               .ppc_sys_name   = "8347",
+               .mask           = 0xFFFF0000,
+               .value          = 0x80530000,
+               .num_devices    = 7,
+               .device_list    = (enum ppc_sys_devices[])
+               {
+                       MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
+                       MPC83xx_IIC2, MPC83xx_DUART,
+                       MPC83xx_USB2_DR, MPC83xx_USB2_MPH
+               },
+       },
+       {
+               .ppc_sys_name   = "8343E",
+               .mask           = 0xFFFF0000,
+               .value          = 0x80540000,
+               .num_devices    = 7,
+               .device_list    = (enum ppc_sys_devices[])
+               {
+                       MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
+                       MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2,
+                       MPC83xx_USB2_DR,
+               },
+       },
+       {
+               .ppc_sys_name   = "8343",
+               .mask           = 0xFFFF0000,
+               .value          = 0x80550000,
+               .num_devices    = 6,
+               .device_list    = (enum ppc_sys_devices[])
+               {
+                       MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
+                       MPC83xx_IIC2, MPC83xx_DUART,
+                       MPC83xx_USB2_DR,
+               },
+       },
+       {       /* default match */
+               .ppc_sys_name   = "",
+               .mask           = 0x00000000,
+               .value          = 0x00000000,
+       },
+};
diff --git a/arch/ppc/syslib/mpc85xx_devices.c b/arch/ppc/syslib/mpc85xx_devices.c
new file mode 100644 (file)
index 0000000..1e658ef
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * 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/syslib/mpc85xx_sys.c b/arch/ppc/syslib/mpc85xx_sys.c
new file mode 100644 (file)
index 0000000..d806a92
--- /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   = "8540",
+               .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   = "8560",
+               .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   = "8541",
+               .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   = "8541E",
+               .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   = "8555",
+               .mask           = 0xFFFF0000,
+               .value          = 0x80710000,
+               .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_DUART,
+                       MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1,
+                       MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3,
+                       MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
+                       MPC85xx_CPM_SMC1, MPC85xx_CPM_SMC2,
+                       MPC85xx_CPM_USB,
+               },
+       },
+       {
+               .ppc_sys_name   = "8555E",
+               .mask           = 0xFFFF0000,
+               .value          = 0x80790000,
+               .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_SEC2,
+                       MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1,
+                       MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3,
+                       MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
+                       MPC85xx_CPM_SMC1, MPC85xx_CPM_SMC2,
+                       MPC85xx_CPM_USB,
+               },
+       },
+       {       /* default match */
+               .ppc_sys_name   = "",
+               .mask           = 0x00000000,
+               .value          = 0x00000000,
+       },
+};
diff --git a/arch/ppc/syslib/ppc83xx_setup.c b/arch/ppc/syslib/ppc83xx_setup.c
new file mode 100644 (file)
index 0000000..843cf88
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * arch/ppc/syslib/ppc83xx_setup.c
+ *
+ * MPC83XX common board code
+ *
+ * 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/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/serial.h>
+#include <linux/tty.h> /* for linux/serial_core.h */
+#include <linux/serial_core.h>
+#include <linux/serial_8250.h>
+
+#include <asm/prom.h>
+#include <asm/time.h>
+#include <asm/mpc83xx.h>
+#include <asm/mmu.h>
+#include <asm/ppc_sys.h>
+#include <asm/kgdb.h>
+#include <asm/delay.h>
+
+#include <syslib/ppc83xx_setup.h>
+
+phys_addr_t immrbar;
+
+/* Return the amount of memory */
+unsigned long __init
+mpc83xx_find_end_of_memory(void)
+{
+        bd_t *binfo;
+
+        binfo = (bd_t *) __res;
+
+        return binfo->bi_memsize;
+}
+
+long __init
+mpc83xx_time_init(void)
+{
+#define SPCR_OFFS   0x00000110
+#define SPCR_TBEN   0x00400000
+
+       bd_t *binfo = (bd_t *)__res;
+       u32 *spcr = ioremap(binfo->bi_immr_base + SPCR_OFFS, 4);
+
+       *spcr |= SPCR_TBEN;
+
+       iounmap(spcr);
+
+       return 0;
+}
+
+/* The decrementer counts at the system (internal) clock freq divided by 4 */
+void __init
+mpc83xx_calibrate_decr(void)
+{
+        bd_t *binfo = (bd_t *) __res;
+        unsigned int freq, divisor;
+
+       freq = binfo->bi_busfreq;
+       divisor = 4;
+       tb_ticks_per_jiffy = freq / HZ / divisor;
+       tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000);
+}
+
+#ifdef CONFIG_SERIAL_8250
+void __init
+mpc83xx_early_serial_map(void)
+{
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+       struct uart_port serial_req;
+#endif
+       struct plat_serial8250_port *pdata;
+       bd_t *binfo = (bd_t *) __res;
+       pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(MPC83xx_DUART);
+
+       /* Setup serial port access */
+       pdata[0].uartclk = binfo->bi_busfreq;
+       pdata[0].mapbase += binfo->bi_immr_base;
+       pdata[0].membase = ioremap(pdata[0].mapbase, 0x100);
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+       memset(&serial_req, 0, sizeof (serial_req));
+       serial_req.iotype = SERIAL_IO_MEM;
+       serial_req.mapbase = pdata[0].mapbase;
+       serial_req.membase = pdata[0].membase;
+       serial_req.regshift = 0;
+
+       gen550_init(0, &serial_req);
+#endif
+
+       pdata[1].uartclk = binfo->bi_busfreq;
+       pdata[1].mapbase += binfo->bi_immr_base;
+       pdata[1].membase = ioremap(pdata[1].mapbase, 0x100);
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+       /* Assume gen550_init() doesn't modify serial_req */
+       serial_req.mapbase = pdata[1].mapbase;
+       serial_req.membase = pdata[1].membase;
+
+       gen550_init(1, &serial_req);
+#endif
+}
+#endif
+
+void
+mpc83xx_restart(char *cmd)
+{
+       volatile unsigned char __iomem *reg;
+       unsigned char tmp;
+
+       reg = ioremap(BCSR_PHYS_ADDR, BCSR_SIZE);
+
+       local_irq_disable();
+
+       /*
+        * Unlock the BCSR bits so a PRST will update the contents.
+        * Otherwise the reset asserts but doesn't clear.
+        */
+       tmp = in_8(reg + BCSR_MISC_REG3_OFF);
+       tmp |= BCSR_MISC_REG3_CNFLOCK; /* low true, high false */
+       out_8(reg + BCSR_MISC_REG3_OFF, tmp);
+
+       /*
+        * Trigger a reset via a low->high transition of the
+        * PORESET bit.
+        */
+       tmp = in_8(reg + BCSR_MISC_REG2_OFF);
+       tmp &= ~BCSR_MISC_REG2_PORESET;
+       out_8(reg + BCSR_MISC_REG2_OFF, tmp);
+
+       udelay(1);
+
+       tmp |= BCSR_MISC_REG2_PORESET;
+       out_8(reg + BCSR_MISC_REG2_OFF, tmp);
+
+       for(;;);
+}
+
+void
+mpc83xx_power_off(void)
+{
+       local_irq_disable();
+       for(;;);
+}
+
+void
+mpc83xx_halt(void)
+{
+       local_irq_disable();
+       for(;;);
+}
+
+/* PCI SUPPORT DOES NOT EXIT, MODEL after ppc85xx_setup.c */
diff --git a/arch/ppc/syslib/ppc83xx_setup.h b/arch/ppc/syslib/ppc83xx_setup.h
new file mode 100644 (file)
index 0000000..683f179
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * arch/ppc/syslib/ppc83xx_setup.h
+ *
+ * MPC83XX common board definitions
+ *
+ * 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.
+ *
+ */
+
+#ifndef __PPC_SYSLIB_PPC83XX_SETUP_H
+#define __PPC_SYSLIB_PPC83XX_SETUP_H
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <asm/ppcboot.h>
+
+extern unsigned long mpc83xx_find_end_of_memory(void) __init;
+extern long mpc83xx_time_init(void) __init;
+extern void mpc83xx_calibrate_decr(void) __init;
+extern void mpc83xx_early_serial_map(void) __init;
+extern void mpc83xx_restart(char *cmd);
+extern void mpc83xx_power_off(void);
+extern void mpc83xx_halt(void);
+extern void mpc83xx_setup_hose(void) __init;
+
+/* PCI config */
+#if 0
+#define PCI1_CFG_ADDR_OFFSET   (FIXME)
+#define PCI1_CFG_DATA_OFFSET   (FIXME)
+
+#define PCI2_CFG_ADDR_OFFSET   (FIXME)
+#define PCI2_CFG_DATA_OFFSET   (FIXME)
+#endif
+
+/* Serial Config */
+#ifdef CONFIG_SERIAL_MANY_PORTS
+#define RS_TABLE_SIZE  64
+#else
+#define RS_TABLE_SIZE  2
+#endif
+
+#ifndef BASE_BAUD
+#define BASE_BAUD 115200
+#endif
+
+#endif /* __PPC_SYSLIB_PPC83XX_SETUP_H */
diff --git a/arch/ppc64/kernel/pSeries_reconfig.c b/arch/ppc64/kernel/pSeries_reconfig.c
new file mode 100644 (file)
index 0000000..dc2a69d
--- /dev/null
@@ -0,0 +1,426 @@
+/*
+ * pSeries_reconfig.c - support for dynamic reconfiguration (including PCI
+ * Hotplug and Dynamic Logical Partitioning on RPA platforms).
+ *
+ * Copyright (C) 2005 Nathan Lynch
+ * Copyright (C) 2005 IBM 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kref.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+
+#include <asm/prom.h>
+#include <asm/pSeries_reconfig.h>
+#include <asm/uaccess.h>
+
+
+
+/*
+ * Routines for "runtime" addition and removal of device tree nodes.
+ */
+#ifdef CONFIG_PROC_DEVICETREE
+/*
+ * Add a node to /proc/device-tree.
+ */
+static void add_node_proc_entries(struct device_node *np)
+{
+       struct proc_dir_entry *ent;
+
+       ent = proc_mkdir(strrchr(np->full_name, '/') + 1, np->parent->pde);
+       if (ent)
+               proc_device_tree_add_node(np, ent);
+}
+
+static void remove_node_proc_entries(struct device_node *np)
+{
+       struct property *pp = np->properties;
+       struct device_node *parent = np->parent;
+
+       while (pp) {
+               remove_proc_entry(pp->name, np->pde);
+               pp = pp->next;
+       }
+       if (np->pde)
+               remove_proc_entry(np->pde->name, parent->pde);
+}
+#else /* !CONFIG_PROC_DEVICETREE */
+static void add_node_proc_entries(struct device_node *np)
+{
+       return;
+}
+
+static void remove_node_proc_entries(struct device_node *np)
+{
+       return;
+}
+#endif /* CONFIG_PROC_DEVICETREE */
+
+/**
+ *     derive_parent - basically like dirname(1)
+ *     @path:  the full_name of a node to be added to the tree
+ *
+ *     Returns the node which should be the parent of the node
+ *     described by path.  E.g., for path = "/foo/bar", returns
+ *     the node with full_name = "/foo".
+ */
+static struct device_node *derive_parent(const char *path)
+{
+       struct device_node *parent = NULL;
+       char *parent_path = "/";
+       size_t parent_path_len = strrchr(path, '/') - path + 1;
+
+       /* reject if path is "/" */
+       if (!strcmp(path, "/"))
+               return ERR_PTR(-EINVAL);
+
+       if (strrchr(path, '/') != path) {
+               parent_path = kmalloc(parent_path_len, GFP_KERNEL);
+               if (!parent_path)
+                       return ERR_PTR(-ENOMEM);
+               strlcpy(parent_path, path, parent_path_len);
+       }
+       parent = of_find_node_by_path(parent_path);
+       if (!parent)
+               return ERR_PTR(-EINVAL);
+       if (strcmp(parent_path, "/"))
+               kfree(parent_path);
+       return parent;
+}
+
+static struct notifier_block *pSeries_reconfig_chain;
+
+int pSeries_reconfig_notifier_register(struct notifier_block *nb)
+{
+       return notifier_chain_register(&pSeries_reconfig_chain, nb);
+}
+
+void pSeries_reconfig_notifier_unregister(struct notifier_block *nb)
+{
+       notifier_chain_unregister(&pSeries_reconfig_chain, nb);
+}
+
+static int pSeries_reconfig_add_node(const char *path, struct property *proplist)
+{
+       struct device_node *np;
+       int err = -ENOMEM;
+
+       np = kcalloc(1, sizeof(*np), GFP_KERNEL);
+       if (!np)
+               goto out_err;
+
+       np->full_name = kmalloc(strlen(path) + 1, GFP_KERNEL);
+       if (!np->full_name)
+               goto out_err;
+
+       strcpy(np->full_name, path);
+
+       np->properties = proplist;
+       OF_MARK_DYNAMIC(np);
+       kref_init(&np->kref);
+
+       np->parent = derive_parent(path);
+       if (IS_ERR(np->parent)) {
+               err = PTR_ERR(np->parent);
+               goto out_err;
+       }
+
+       err = notifier_call_chain(&pSeries_reconfig_chain,
+                                 PSERIES_RECONFIG_ADD, np);
+       if (err == NOTIFY_BAD) {
+               printk(KERN_ERR "Failed to add device node %s\n", path);
+               err = -ENOMEM; /* For now, safe to assume kmalloc failure */
+               goto out_err;
+       }
+
+       of_attach_node(np);
+
+       add_node_proc_entries(np);
+
+       of_node_put(np->parent);
+
+       return 0;
+
+out_err:
+       if (np) {
+               of_node_put(np->parent);
+               kfree(np->full_name);
+               kfree(np);
+       }
+       return err;
+}
+
+static int pSeries_reconfig_remove_node(struct device_node *np)
+{
+       struct device_node *parent, *child;
+
+       parent = of_get_parent(np);
+       if (!parent)
+               return -EINVAL;
+
+       if ((child = of_get_next_child(np, NULL))) {
+               of_node_put(child);
+               return -EBUSY;
+       }
+
+       remove_node_proc_entries(np);
+
+       notifier_call_chain(&pSeries_reconfig_chain,
+                           PSERIES_RECONFIG_REMOVE, np);
+       of_detach_node(np);
+
+       of_node_put(parent);
+       of_node_put(np); /* Must decrement the refcount */
+       return 0;
+}
+
+/*
+ * /proc/ppc64/ofdt - yucky binary interface for adding and removing
+ * OF device nodes.  Should be deprecated as soon as we get an
+ * in-kernel wrapper for the RTAS ibm,configure-connector call.
+ */
+
+static void release_prop_list(const struct property *prop)
+{
+       struct property *next;
+       for (; prop; prop = next) {
+               next = prop->next;
+               kfree(prop->name);
+               kfree(prop->value);
+               kfree(prop);
+       }
+
+}
+
+/**
+ * parse_next_property - process the next property from raw input buffer
+ * @buf: input buffer, must be nul-terminated
+ * @end: end of the input buffer + 1, for validation
+ * @name: return value; set to property name in buf
+ * @length: return value; set to length of value
+ * @value: return value; set to the property value in buf
+ *
+ * Note that the caller must make copies of the name and value returned,
+ * this function does no allocation or copying of the data.  Return value
+ * is set to the next name in buf, or NULL on error.
+ */
+static char * parse_next_property(char *buf, char *end, char **name, int *length,
+                                 unsigned char **value)
+{
+       char *tmp;
+
+       *name = buf;
+
+       tmp = strchr(buf, ' ');
+       if (!tmp) {
+               printk(KERN_ERR "property parse failed in %s at line %d\n",
+                      __FUNCTION__, __LINE__);
+               return NULL;
+       }
+       *tmp = '\0';
+
+       if (++tmp >= end) {
+               printk(KERN_ERR "property parse failed in %s at line %d\n",
+                      __FUNCTION__, __LINE__);
+               return NULL;
+       }
+
+       /* now we're on the length */
+       *length = -1;
+       *length = simple_strtoul(tmp, &tmp, 10);
+       if (*length == -1) {
+               printk(KERN_ERR "property parse failed in %s at line %d\n",
+                      __FUNCTION__, __LINE__);
+               return NULL;
+       }
+       if (*tmp != ' ' || ++tmp >= end) {
+               printk(KERN_ERR "property parse failed in %s at line %d\n",
+                      __FUNCTION__, __LINE__);
+               return NULL;
+       }
+
+       /* now we're on the value */
+       *value = tmp;
+       tmp += *length;
+       if (tmp > end) {
+               printk(KERN_ERR "property parse failed in %s at line %d\n",
+                      __FUNCTION__, __LINE__);
+               return NULL;
+       }
+       else if (tmp < end && *tmp != ' ' && *tmp != '\0') {
+               printk(KERN_ERR "property parse failed in %s at line %d\n",
+                      __FUNCTION__, __LINE__);
+               return NULL;
+       }
+       tmp++;
+
+       /* and now we should be on the next name, or the end */
+       return tmp;
+}
+
+static struct property *new_property(const char *name, const int length,
+                                    const unsigned char *value, struct property *last)
+{
+       struct property *new = kmalloc(sizeof(*new), GFP_KERNEL);
+
+       if (!new)
+               return NULL;
+       memset(new, 0, sizeof(*new));
+
+       if (!(new->name = kmalloc(strlen(name) + 1, GFP_KERNEL)))
+               goto cleanup;
+       if (!(new->value = kmalloc(length + 1, GFP_KERNEL)))
+               goto cleanup;
+
+       strcpy(new->name, name);
+       memcpy(new->value, value, length);
+       *(((char *)new->value) + length) = 0;
+       new->length = length;
+       new->next = last;
+       return new;
+
+cleanup:
+       if (new->name)
+               kfree(new->name);
+       if (new->value)
+               kfree(new->value);
+       kfree(new);
+       return NULL;
+}
+
+static int do_add_node(char *buf, size_t bufsize)
+{
+       char *path, *end, *name;
+       struct device_node *np;
+       struct property *prop = NULL;
+       unsigned char* value;
+       int length, rv = 0;
+
+       end = buf + bufsize;
+       path = buf;
+       buf = strchr(buf, ' ');
+       if (!buf)
+               return -EINVAL;
+       *buf = '\0';
+       buf++;
+
+       if ((np = of_find_node_by_path(path))) {
+               of_node_put(np);
+               return -EINVAL;
+       }
+
+       /* rv = build_prop_list(tmp, bufsize - (tmp - buf), &proplist); */
+       while (buf < end &&
+              (buf = parse_next_property(buf, end, &name, &length, &value))) {
+               struct property *last = prop;
+
+               prop = new_property(name, length, value, last);
+               if (!prop) {
+                       rv = -ENOMEM;
+                       prop = last;
+                       goto out;
+               }
+       }
+       if (!buf) {
+               rv = -EINVAL;
+               goto out;
+       }
+
+       rv = pSeries_reconfig_add_node(path, prop);
+
+out:
+       if (rv)
+               release_prop_list(prop);
+       return rv;
+}
+
+static int do_remove_node(char *buf)
+{
+       struct device_node *node;
+       int rv = -ENODEV;
+
+       if ((node = of_find_node_by_path(buf)))
+               rv = pSeries_reconfig_remove_node(node);
+
+       of_node_put(node);
+       return rv;
+}
+
+/**
+ * ofdt_write - perform operations on the Open Firmware device tree
+ *
+ * @file: not used
+ * @buf: command and arguments
+ * @count: size of the command buffer
+ * @off: not used
+ *
+ * Operations supported at this time are addition and removal of
+ * whole nodes along with their properties.  Operations on individual
+ * properties are not implemented (yet).
+ */
+static ssize_t ofdt_write(struct file *file, const char __user *buf, size_t count,
+                         loff_t *off)
+{
+       int rv = 0;
+       char *kbuf;
+       char *tmp;
+
+       if (!(kbuf = kmalloc(count + 1, GFP_KERNEL))) {
+               rv = -ENOMEM;
+               goto out;
+       }
+       if (copy_from_user(kbuf, buf, count)) {
+               rv = -EFAULT;
+               goto out;
+       }
+
+       kbuf[count] = '\0';
+
+       tmp = strchr(kbuf, ' ');
+       if (!tmp) {
+               rv = -EINVAL;
+               goto out;
+       }
+       *tmp = '\0';
+       tmp++;
+
+       if (!strcmp(kbuf, "add_node"))
+               rv = do_add_node(tmp, count - (tmp - kbuf));
+       else if (!strcmp(kbuf, "remove_node"))
+               rv = do_remove_node(tmp);
+       else
+               rv = -EINVAL;
+out:
+       kfree(kbuf);
+       return rv ? rv : count;
+}
+
+static struct file_operations ofdt_fops = {
+       .write = ofdt_write
+};
+
+/* create /proc/ppc64/ofdt write-only by root */
+static int proc_ppc64_create_ofdt(void)
+{
+       struct proc_dir_entry *ent;
+
+       if (!(systemcfg->platform & PLATFORM_PSERIES))
+               return 0;
+
+       ent = create_proc_entry("ppc64/ofdt", S_IWUSR, NULL);
+       if (ent) {
+               ent->nlink = 1;
+               ent->data = NULL;
+               ent->size = 0;
+               ent->proc_fops = &ofdt_fops;
+       }
+
+       return 0;
+}
+__initcall(proc_ppc64_create_ofdt);
diff --git a/arch/ppc64/kernel/pmc.c b/arch/ppc64/kernel/pmc.c
new file mode 100644 (file)
index 0000000..67be773
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *  linux/arch/ppc64/kernel/pmc.c
+ *
+ *  Copyright (C) 2004 David Gibson, 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.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+
+#include <asm/processor.h>
+#include <asm/pmc.h>
+
+/* Ensure exceptions are disabled */
+static void dummy_perf(struct pt_regs *regs)
+{
+       unsigned int mmcr0 = mfspr(SPRN_MMCR0);
+
+       mmcr0 &= ~(MMCR0_PMXE|MMCR0_PMAO);
+       mtspr(SPRN_MMCR0, mmcr0);
+}
+
+static spinlock_t pmc_owner_lock = SPIN_LOCK_UNLOCKED;
+static void *pmc_owner_caller; /* mostly for debugging */
+perf_irq_t perf_irq = dummy_perf;
+
+int reserve_pmc_hardware(perf_irq_t new_perf_irq)
+{
+       int err = 0;
+
+       spin_lock(&pmc_owner_lock);
+
+       if (pmc_owner_caller) {
+               printk(KERN_WARNING "reserve_pmc_hardware: "
+                      "PMC hardware busy (reserved by caller %p)\n",
+                      pmc_owner_caller);
+               err = -EBUSY;
+               goto out;
+       }
+
+       pmc_owner_caller = __builtin_return_address(0);
+       perf_irq = new_perf_irq ? : dummy_perf;
+
+ out:
+       spin_unlock(&pmc_owner_lock);
+       return err;
+}
+EXPORT_SYMBOL_GPL(reserve_pmc_hardware);
+
+void release_pmc_hardware(void)
+{
+       spin_lock(&pmc_owner_lock);
+
+       WARN_ON(! pmc_owner_caller);
+
+       pmc_owner_caller = NULL;
+       perf_irq = dummy_perf;
+
+       spin_unlock(&pmc_owner_lock);
+}
+EXPORT_SYMBOL_GPL(release_pmc_hardware);
diff --git a/arch/ppc64/kernel/vdso.c b/arch/ppc64/kernel/vdso.c
new file mode 100644 (file)
index 0000000..4777676
--- /dev/null
@@ -0,0 +1,619 @@
+/*
+ *  linux/arch/ppc64/kernel/vdso.c
+ *
+ *    Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp.
+ *                      <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 <linux/module.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/slab.h>
+#include <linux/user.h>
+#include <linux/elf.h>
+#include <linux/security.h>
+#include <linux/bootmem.h>
+
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/mmu.h>
+#include <asm/mmu_context.h>
+#include <asm/machdep.h>
+#include <asm/cputable.h>
+#include <asm/sections.h>
+#include <asm/vdso.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt...) printk(fmt)
+#else
+#define DBG(fmt...)
+#endif
+
+
+/*
+ * The vDSOs themselves are here
+ */
+extern char vdso64_start, vdso64_end;
+extern char vdso32_start, vdso32_end;
+
+static void *vdso64_kbase = &vdso64_start;
+static void *vdso32_kbase = &vdso32_start;
+
+unsigned int vdso64_pages;
+unsigned int vdso32_pages;
+
+/* Signal trampolines user addresses */
+
+unsigned long vdso64_rt_sigtramp;
+unsigned long vdso32_sigtramp;
+unsigned long vdso32_rt_sigtramp;
+
+/* Format of the patch table */
+struct vdso_patch_def
+{
+       u32             pvr_mask, pvr_value;
+       const char      *gen_name;
+       const char      *fix_name;
+};
+
+/* Table of functions to patch based on the CPU type/revision
+ *
+ * TODO: Improve by adding whole lists for each entry
+ */
+static struct vdso_patch_def vdso_patches[] = {
+       {
+               0xffff0000, 0x003a0000,         /* POWER5 */
+               "__kernel_sync_dicache", "__kernel_sync_dicache_p5"
+       },
+       {
+               0xffff0000, 0x003b0000,         /* POWER5 */
+               "__kernel_sync_dicache", "__kernel_sync_dicache_p5"
+       },
+};
+
+/*
+ * Some infos carried around for each of them during parsing at
+ * boot time.
+ */
+struct lib32_elfinfo
+{
+       Elf32_Ehdr      *hdr;           /* ptr to ELF */
+       Elf32_Sym       *dynsym;        /* ptr to .dynsym section */
+       unsigned long   dynsymsize;     /* size of .dynsym section */
+       char            *dynstr;        /* ptr to .dynstr section */
+       unsigned long   text;           /* offset of .text section in .so */
+};
+
+struct lib64_elfinfo
+{
+       Elf64_Ehdr      *hdr;
+       Elf64_Sym       *dynsym;
+       unsigned long   dynsymsize;
+       char            *dynstr;
+       unsigned long   text;
+};
+
+
+#ifdef __DEBUG
+static void dump_one_vdso_page(struct page *pg, struct page *upg)
+{
+       printk("kpg: %p (c:%d,f:%08lx)", __va(page_to_pfn(pg) << PAGE_SHIFT),
+              page_count(pg),
+              pg->flags);
+       if (upg/* && pg != upg*/) {
+               printk(" upg: %p (c:%d,f:%08lx)", __va(page_to_pfn(upg) << PAGE_SHIFT),
+                      page_count(upg),
+                      upg->flags);
+       }
+       printk("\n");
+}
+
+static void dump_vdso_pages(struct vm_area_struct * vma)
+{
+       int i;
+
+       if (!vma || test_thread_flag(TIF_32BIT)) {
+               printk("vDSO32 @ %016lx:\n", (unsigned long)vdso32_kbase);
+               for (i=0; i<vdso32_pages; i++) {
+                       struct page *pg = virt_to_page(vdso32_kbase + i*PAGE_SIZE);
+                       struct page *upg = (vma && vma->vm_mm) ?
+                               follow_page(vma->vm_mm, vma->vm_start + i*PAGE_SIZE, 0)
+                               : NULL;
+                       dump_one_vdso_page(pg, upg);
+               }
+       }
+       if (!vma || !test_thread_flag(TIF_32BIT)) {
+               printk("vDSO64 @ %016lx:\n", (unsigned long)vdso64_kbase);
+               for (i=0; i<vdso64_pages; i++) {
+                       struct page *pg = virt_to_page(vdso64_kbase + i*PAGE_SIZE);
+                       struct page *upg = (vma && vma->vm_mm) ?
+                               follow_page(vma->vm_mm, vma->vm_start + i*PAGE_SIZE, 0)
+                               : NULL;
+                       dump_one_vdso_page(pg, upg);
+               }
+       }
+}
+#endif /* DEBUG */
+
+/*
+ * Keep a dummy vma_close for now, it will prevent VMA merging.
+ */
+static void vdso_vma_close(struct vm_area_struct * vma)
+{
+}
+
+/*
+ * Our nopage() function, maps in the actual vDSO kernel pages, they will
+ * be mapped read-only by do_no_page(), and eventually COW'ed, either
+ * right away for an initial write access, or by do_wp_page().
+ */
+static struct page * vdso_vma_nopage(struct vm_area_struct * vma,
+                                    unsigned long address, int *type)
+{
+       unsigned long offset = address - vma->vm_start;
+       struct page *pg;
+       void *vbase = test_thread_flag(TIF_32BIT) ? vdso32_kbase : vdso64_kbase;
+
+       DBG("vdso_vma_nopage(current: %s, address: %016lx, off: %lx)\n",
+           current->comm, address, offset);
+
+       if (address < vma->vm_start || address > vma->vm_end)
+               return NOPAGE_SIGBUS;
+
+       /*
+        * Last page is systemcfg, special handling here, no get_page() a
+        * this is a reserved page
+        */
+       if ((vma->vm_end - address) <= PAGE_SIZE)
+               return virt_to_page(systemcfg);
+
+       pg = virt_to_page(vbase + offset);
+       get_page(pg);
+       DBG(" ->page count: %d\n", page_count(pg));
+
+       return pg;
+}
+
+static struct vm_operations_struct vdso_vmops = {
+       .close  = vdso_vma_close,
+       .nopage = vdso_vma_nopage,
+};
+
+/*
+ * This is called from binfmt_elf, we create the special vma for the
+ * vDSO and insert it into the mm struct tree
+ */
+int arch_setup_additional_pages(struct linux_binprm *bprm, int executable_stack)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+       unsigned long vdso_pages;
+       unsigned long vdso_base;
+
+       if (test_thread_flag(TIF_32BIT)) {
+               vdso_pages = vdso32_pages;
+               vdso_base = VDSO32_MBASE;
+       } else {
+               vdso_pages = vdso64_pages;
+               vdso_base = VDSO64_MBASE;
+       }
+
+       current->thread.vdso_base = 0;
+
+       /* vDSO has a problem and was disabled, just don't "enable" it for the
+        * process
+        */
+       if (vdso_pages == 0)
+               return 0;
+
+       vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+       if (vma == NULL)
+               return -ENOMEM;
+       if (security_vm_enough_memory(vdso_pages)) {
+               kmem_cache_free(vm_area_cachep, vma);
+               return -ENOMEM;
+       }
+       memset(vma, 0, sizeof(*vma));
+
+       /*
+        * pick a base address for the vDSO in process space. We try to put it
+        * at vdso_base which is the "natural" base for it, but we might fail
+        * and end up putting it elsewhere.
+        */
+       vdso_base = get_unmapped_area(NULL, vdso_base,
+                                     vdso_pages << PAGE_SHIFT, 0, 0);
+       if (vdso_base & ~PAGE_MASK)
+               return (int)vdso_base;
+
+       current->thread.vdso_base = vdso_base;
+
+       vma->vm_mm = mm;
+       vma->vm_start = current->thread.vdso_base;
+
+       /*
+        * the VMA size is one page more than the vDSO since systemcfg
+        * is mapped in the last one
+        */
+       vma->vm_end = vma->vm_start + ((vdso_pages + 1) << PAGE_SHIFT);
+
+       /*
+        * our vma flags don't have VM_WRITE so by default, the process isn't allowed
+        * to write those pages.
+        * gdb can break that with ptrace interface, and thus trigger COW on those
+        * pages but it's then your responsibility to never do that on the "data" page
+        * of the vDSO or you'll stop getting kernel updates and your nice userland
+        * gettimeofday will be totally dead. It's fine to use that for setting
+        * breakpoints in the vDSO code pages though
+        */
+       vma->vm_flags = VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
+       vma->vm_flags |= mm->def_flags;
+       vma->vm_page_prot = protection_map[vma->vm_flags & 0x7];
+       vma->vm_ops = &vdso_vmops;
+
+       down_write(&mm->mmap_sem);
+       insert_vm_struct(mm, vma);
+       mm->total_vm += (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+       up_write(&mm->mmap_sem);
+
+       return 0;
+}
+
+static void * __init find_section32(Elf32_Ehdr *ehdr, const char *secname,
+                                 unsigned long *size)
+{
+       Elf32_Shdr *sechdrs;
+       unsigned int i;
+       char *secnames;
+
+       /* Grab section headers and strings so we can tell who is who */
+       sechdrs = (void *)ehdr + ehdr->e_shoff;
+       secnames = (void *)ehdr + sechdrs[ehdr->e_shstrndx].sh_offset;
+
+       /* Find the section they want */
+       for (i = 1; i < ehdr->e_shnum; i++) {
+               if (strcmp(secnames+sechdrs[i].sh_name, secname) == 0) {
+                       if (size)
+                               *size = sechdrs[i].sh_size;
+                       return (void *)ehdr + sechdrs[i].sh_offset;
+               }
+       }
+       *size = 0;
+       return NULL;
+}
+
+static void * __init find_section64(Elf64_Ehdr *ehdr, const char *secname,
+                                 unsigned long *size)
+{
+       Elf64_Shdr *sechdrs;
+       unsigned int i;
+       char *secnames;
+
+       /* Grab section headers and strings so we can tell who is who */
+       sechdrs = (void *)ehdr + ehdr->e_shoff;
+       secnames = (void *)ehdr + sechdrs[ehdr->e_shstrndx].sh_offset;
+
+       /* Find the section they want */
+       for (i = 1; i < ehdr->e_shnum; i++) {
+               if (strcmp(secnames+sechdrs[i].sh_name, secname) == 0) {
+                       if (size)
+                               *size = sechdrs[i].sh_size;
+                       return (void *)ehdr + sechdrs[i].sh_offset;
+               }
+       }
+       if (size)
+               *size = 0;
+       return NULL;
+}
+
+static Elf32_Sym * __init find_symbol32(struct lib32_elfinfo *lib, const char *symname)
+{
+       unsigned int i;
+       char name[32], *c;
+
+       for (i = 0; i < (lib->dynsymsize / sizeof(Elf32_Sym)); i++) {
+               if (lib->dynsym[i].st_name == 0)
+                       continue;
+               strlcpy(name, lib->dynstr + lib->dynsym[i].st_name, 32);
+               c = strchr(name, '@');
+               if (c)
+                       *c = 0;
+               if (strcmp(symname, name) == 0)
+                       return &lib->dynsym[i];
+       }
+       return NULL;
+}
+
+static Elf64_Sym * __init find_symbol64(struct lib64_elfinfo *lib, const char *symname)
+{
+       unsigned int i;
+       char name[32], *c;
+
+       for (i = 0; i < (lib->dynsymsize / sizeof(Elf64_Sym)); i++) {
+               if (lib->dynsym[i].st_name == 0)
+                       continue;
+               strlcpy(name, lib->dynstr + lib->dynsym[i].st_name, 32);
+               c = strchr(name, '@');
+               if (c)
+                       *c = 0;
+               if (strcmp(symname, name) == 0)
+                       return &lib->dynsym[i];
+       }
+       return NULL;
+}
+
+/* Note that we assume the section is .text and the symbol is relative to
+ * the library base
+ */
+static unsigned long __init find_function32(struct lib32_elfinfo *lib, const char *symname)
+{
+       Elf32_Sym *sym = find_symbol32(lib, symname);
+
+       if (sym == NULL) {
+               printk(KERN_WARNING "vDSO32: function %s not found !\n", symname);
+               return 0;
+       }
+       return sym->st_value - VDSO32_LBASE;
+}
+
+/* Note that we assume the section is .text and the symbol is relative to
+ * the library base
+ */
+static unsigned long __init find_function64(struct lib64_elfinfo *lib, const char *symname)
+{
+       Elf64_Sym *sym = find_symbol64(lib, symname);
+
+       if (sym == NULL) {
+               printk(KERN_WARNING "vDSO64: function %s not found !\n", symname);
+               return 0;
+       }
+#ifdef VDS64_HAS_DESCRIPTORS
+       return *((u64 *)(vdso64_kbase + sym->st_value - VDSO64_LBASE)) - VDSO64_LBASE;
+#else
+       return sym->st_value - VDSO64_LBASE;
+#endif
+}
+
+
+static __init int vdso_do_find_sections(struct lib32_elfinfo *v32,
+                                       struct lib64_elfinfo *v64)
+{
+       void *sect;
+
+       /*
+        * Locate symbol tables & text section
+        */
+
+       v32->dynsym = find_section32(v32->hdr, ".dynsym", &v32->dynsymsize);
+       v32->dynstr = find_section32(v32->hdr, ".dynstr", NULL);
+       if (v32->dynsym == NULL || v32->dynstr == NULL) {
+               printk(KERN_ERR "vDSO32: a required symbol section was not found\n");
+               return -1;
+       }
+       sect = find_section32(v32->hdr, ".text", NULL);
+       if (sect == NULL) {
+               printk(KERN_ERR "vDSO32: the .text section was not found\n");
+               return -1;
+       }
+       v32->text = sect - vdso32_kbase;
+
+       v64->dynsym = find_section64(v64->hdr, ".dynsym", &v64->dynsymsize);
+       v64->dynstr = find_section64(v64->hdr, ".dynstr", NULL);
+       if (v64->dynsym == NULL || v64->dynstr == NULL) {
+               printk(KERN_ERR "vDSO64: a required symbol section was not found\n");
+               return -1;
+       }
+       sect = find_section64(v64->hdr, ".text", NULL);
+       if (sect == NULL) {
+               printk(KERN_ERR "vDSO64: the .text section was not found\n");
+               return -1;
+       }
+       v64->text = sect - vdso64_kbase;
+
+       return 0;
+}
+
+static __init void vdso_setup_trampolines(struct lib32_elfinfo *v32,
+                                         struct lib64_elfinfo *v64)
+{
+       /*
+        * Find signal trampolines
+        */
+
+       vdso64_rt_sigtramp      = find_function64(v64, "__kernel_sigtramp_rt64");
+       vdso32_sigtramp         = find_function32(v32, "__kernel_sigtramp32");
+       vdso32_rt_sigtramp      = find_function32(v32, "__kernel_sigtramp_rt32");
+}
+
+static __init int vdso_fixup_datapage(struct lib32_elfinfo *v32,
+                                      struct lib64_elfinfo *v64)
+{
+       Elf32_Sym *sym32;
+       Elf64_Sym *sym64;
+
+       sym32 = find_symbol32(v32, "__kernel_datapage_offset");
+       if (sym32 == NULL) {
+               printk(KERN_ERR "vDSO32: Can't find symbol __kernel_datapage_offset !\n");
+               return -1;
+       }
+       *((int *)(vdso32_kbase + (sym32->st_value - VDSO32_LBASE))) =
+               (vdso32_pages << PAGE_SHIFT) - (sym32->st_value - VDSO32_LBASE);
+
+               sym64 = find_symbol64(v64, "__kernel_datapage_offset");
+       if (sym64 == NULL) {
+               printk(KERN_ERR "vDSO64: Can't find symbol __kernel_datapage_offset !\n");
+               return -1;
+       }
+       *((int *)(vdso64_kbase + sym64->st_value - VDSO64_LBASE)) =
+               (vdso64_pages << PAGE_SHIFT) - (sym64->st_value - VDSO64_LBASE);
+
+       return 0;
+}
+
+static int vdso_do_func_patch32(struct lib32_elfinfo *v32,
+                               struct lib64_elfinfo *v64,
+                               const char *orig, const char *fix)
+{
+       Elf32_Sym *sym32_gen, *sym32_fix;
+
+       sym32_gen = find_symbol32(v32, orig);
+       if (sym32_gen == NULL) {
+               printk(KERN_ERR "vDSO32: Can't find symbol %s !\n", orig);
+               return -1;
+       }
+       sym32_fix = find_symbol32(v32, fix);
+       if (sym32_fix == NULL) {
+               printk(KERN_ERR "vDSO32: Can't find symbol %s !\n", fix);
+               return -1;
+       }
+       sym32_gen->st_value = sym32_fix->st_value;
+       sym32_gen->st_size = sym32_fix->st_size;
+       sym32_gen->st_info = sym32_fix->st_info;
+       sym32_gen->st_other = sym32_fix->st_other;
+       sym32_gen->st_shndx = sym32_fix->st_shndx;
+
+       return 0;
+}
+
+static int vdso_do_func_patch64(struct lib32_elfinfo *v32,
+                               struct lib64_elfinfo *v64,
+                               const char *orig, const char *fix)
+{
+       Elf64_Sym *sym64_gen, *sym64_fix;
+
+       sym64_gen = find_symbol64(v64, orig);
+       if (sym64_gen == NULL) {
+               printk(KERN_ERR "vDSO64: Can't find symbol %s !\n", orig);
+               return -1;
+       }
+       sym64_fix = find_symbol64(v64, fix);
+       if (sym64_fix == NULL) {
+               printk(KERN_ERR "vDSO64: Can't find symbol %s !\n", fix);
+               return -1;
+       }
+       sym64_gen->st_value = sym64_fix->st_value;
+       sym64_gen->st_size = sym64_fix->st_size;
+       sym64_gen->st_info = sym64_fix->st_info;
+       sym64_gen->st_other = sym64_fix->st_other;
+       sym64_gen->st_shndx = sym64_fix->st_shndx;
+
+       return 0;
+}
+
+static __init int vdso_fixup_alt_funcs(struct lib32_elfinfo *v32,
+                                      struct lib64_elfinfo *v64)
+{
+       u32 pvr;
+       int i;
+
+       pvr = mfspr(SPRN_PVR);
+       for (i = 0; i < ARRAY_SIZE(vdso_patches); i++) {
+               struct vdso_patch_def *patch = &vdso_patches[i];
+               int match = (pvr & patch->pvr_mask) == patch->pvr_value;
+
+               DBG("patch %d (mask: %x, pvr: %x) : %s\n",
+                   i, patch->pvr_mask, patch->pvr_value, match ? "match" : "skip");
+
+               if (!match)
+                       continue;
+
+               DBG("replacing %s with %s...\n", patch->gen_name, patch->fix_name);
+
+               /*
+                * Patch the 32 bits and 64 bits symbols. Note that we do not patch
+                * the "." symbol on 64 bits. It would be easy to do, but doesn't
+                * seem to be necessary, patching the OPD symbol is enough.
+                */
+               vdso_do_func_patch32(v32, v64, patch->gen_name, patch->fix_name);
+               vdso_do_func_patch64(v32, v64, patch->gen_name, patch->fix_name);
+       }
+
+       return 0;
+}
+
+
+static __init int vdso_setup(void)
+{
+       struct lib32_elfinfo    v32;
+       struct lib64_elfinfo    v64;
+
+       v32.hdr = vdso32_kbase;
+       v64.hdr = vdso64_kbase;
+
+       if (vdso_do_find_sections(&v32, &v64))
+               return -1;
+
+       if (vdso_fixup_datapage(&v32, &v64))
+               return -1;
+
+       if (vdso_fixup_alt_funcs(&v32, &v64))
+               return -1;
+
+       vdso_setup_trampolines(&v32, &v64);
+
+       return 0;
+}
+
+void __init vdso_init(void)
+{
+       int i;
+
+       vdso64_pages = (&vdso64_end - &vdso64_start) >> PAGE_SHIFT;
+       vdso32_pages = (&vdso32_end - &vdso32_start) >> PAGE_SHIFT;
+
+       DBG("vdso64_kbase: %p, 0x%x pages, vdso32_kbase: %p, 0x%x pages\n",
+              vdso64_kbase, vdso64_pages, vdso32_kbase, vdso32_pages);
+
+       /*
+        * Initialize the vDSO images in memory, that is do necessary
+        * fixups of vDSO symbols, locate trampolines, etc...
+        */
+       if (vdso_setup()) {
+               printk(KERN_ERR "vDSO setup failure, not enabled !\n");
+               /* XXX should free pages here ? */
+               vdso64_pages = vdso32_pages = 0;
+               return;
+       }
+
+       /* Make sure pages are in the correct state */
+       for (i = 0; i < vdso64_pages; i++) {
+               struct page *pg = virt_to_page(vdso64_kbase + i*PAGE_SIZE);
+               ClearPageReserved(pg);
+               get_page(pg);
+       }
+       for (i = 0; i < vdso32_pages; i++) {
+               struct page *pg = virt_to_page(vdso32_kbase + i*PAGE_SIZE);
+               ClearPageReserved(pg);
+               get_page(pg);
+       }
+}
+
+int in_gate_area_no_task(unsigned long addr)
+{
+       return 0;
+}
+
+int in_gate_area(struct task_struct *task, unsigned long addr)
+{
+       return 0;
+}
+
+struct vm_area_struct *get_gate_vma(struct task_struct *tsk)
+{
+       return NULL;
+}
+
diff --git a/arch/ppc64/kernel/vdso32/Makefile b/arch/ppc64/kernel/vdso32/Makefile
new file mode 100644 (file)
index 0000000..0b1b0df
--- /dev/null
@@ -0,0 +1,36 @@
+
+# List of files in the vdso, has to be asm only for now
+
+obj-vdso32 = sigtramp.o gettimeofday.o datapage.o cacheflush.o note.o
+
+# Build rules
+
+targets := $(obj-vdso32) vdso32.so
+obj-vdso32 := $(addprefix $(obj)/, $(obj-vdso32))
+
+
+EXTRA_CFLAGS := -shared -s -fno-common -fno-builtin
+EXTRA_CFLAGS += -nostdlib -Wl,-soname=linux-vdso32.so.1
+EXTRA_AFLAGS := -D__VDSO32__ -s
+
+obj-y += vdso32_wrapper.o
+extra-y += vdso32.lds
+CPPFLAGS_vdso32.lds += -P -C -U$(ARCH)
+
+# Force dependency (incbin is bad)
+$(obj)/vdso32_wrapper.o : $(obj)/vdso32.so
+
+# link rule for the .so file, .lds has to be first
+$(obj)/vdso32.so: $(src)/vdso32.lds $(obj-vdso32)
+       $(call if_changed,vdso32ld)
+
+# assembly rules for the .S files
+$(obj-vdso32): %.o: %.S
+       $(call if_changed_dep,vdso32as)
+
+# actual build commands
+quiet_cmd_vdso32ld = VDSO32L $@
+      cmd_vdso32ld = $(CROSS32CC) $(c_flags) -Wl,-T $^ -o $@
+quiet_cmd_vdso32as = VDSO32A $@
+      cmd_vdso32as = $(CROSS32CC) $(a_flags) -c -o $@ $<
+
diff --git a/arch/ppc64/kernel/vdso32/cacheflush.S b/arch/ppc64/kernel/vdso32/cacheflush.S
new file mode 100644 (file)
index 0000000..0ed7ea7
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * vDSO provided cache flush routines
+ *
+ * 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.
+ */
+#include <linux/config.h>
+#include <asm/processor.h>
+#include <asm/ppc_asm.h>
+#include <asm/vdso.h>
+#include <asm/offsets.h>
+
+       .text
+
+/*
+ * Default "generic" version of __kernel_sync_dicache.
+ *
+ * void __kernel_sync_dicache(unsigned long start, unsigned long end)
+ *
+ * Flushes the data cache & invalidate the instruction cache for the
+ * provided range [start, end[
+ *
+ * Note: all CPUs supported by this kernel have a 128 bytes cache
+ * line size so we don't have to peek that info from the datapage
+ */
+V_FUNCTION_BEGIN(__kernel_sync_dicache)
+  .cfi_startproc
+       li      r5,127
+       andc    r6,r3,r5                /* round low to line bdy */
+       subf    r8,r6,r4                /* compute length */
+       add     r8,r8,r5                /* ensure we get enough */
+       srwi.   r8,r8,7                 /* compute line count */
+       beqlr                           /* nothing to do? */
+       mtctr   r8
+       mr      r3,r6
+1:     dcbst   0,r3
+       addi    r3,r3,128
+       bdnz    1b
+       sync
+       mtctr   r8
+1:     icbi    0,r6
+       addi    r6,r6,128
+       bdnz    1b
+       isync
+       li      r3,0
+       blr
+  .cfi_endproc
+V_FUNCTION_END(__kernel_sync_dicache)
+
+
+/*
+ * POWER5 version of __kernel_sync_dicache
+ */
+V_FUNCTION_BEGIN(__kernel_sync_dicache_p5)
+  .cfi_startproc
+       sync
+       isync
+       li      r3,0
+       blr
+  .cfi_endproc
+V_FUNCTION_END(__kernel_sync_dicache_p5)
+
diff --git a/arch/ppc64/kernel/vdso32/datapage.S b/arch/ppc64/kernel/vdso32/datapage.S
new file mode 100644 (file)
index 0000000..29b6bd3
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Access to the shared data page by the vDSO & syscall map
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <asm/processor.h>
+#include <asm/ppc_asm.h>
+#include <asm/offsets.h>
+#include <asm/unistd.h>
+#include <asm/vdso.h>
+
+       .text
+V_FUNCTION_BEGIN(__get_datapage)
+  .cfi_startproc
+       /* We don't want that exposed or overridable as we want other objects
+        * to be able to bl directly to here
+        */
+       .protected __get_datapage
+       .hidden __get_datapage
+
+       mflr    r0
+  .cfi_register lr,r0
+
+       bcl     20,31,1f
+       .global __kernel_datapage_offset;
+__kernel_datapage_offset:
+       .long   0
+1:
+       mflr    r3
+       mtlr    r0
+       lwz     r0,0(r3)
+       add     r3,r0,r3
+       blr
+  .cfi_endproc
+V_FUNCTION_END(__get_datapage)
+
+/*
+ * void *__kernel_get_syscall_map(unsigned int *syscall_count) ;
+ *
+ * returns a pointer to the syscall map. the map is agnostic to the
+ * size of "long", unlike kernel bitops, it stores bits from top to
+ * bottom so that memory actually contains a linear bitmap
+ * check for syscall N by testing bit (0x80000000 >> (N & 0x1f)) of
+ * 32 bits int at N >> 5.
+ */
+V_FUNCTION_BEGIN(__kernel_get_syscall_map)
+  .cfi_startproc
+       mflr    r12
+  .cfi_register lr,r12
+
+       mr      r4,r3
+       bl      __get_datapage@local
+       mtlr    r12
+       addi    r3,r3,CFG_SYSCALL_MAP32
+       cmpli   cr0,r4,0
+       beqlr
+       li      r0,__NR_syscalls
+       stw     r0,0(r4)
+       blr
+  .cfi_endproc
+V_FUNCTION_END(__kernel_get_syscall_map)
diff --git a/arch/ppc64/kernel/vdso32/gettimeofday.S b/arch/ppc64/kernel/vdso32/gettimeofday.S
new file mode 100644 (file)
index 0000000..2b48bf1
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Userland implementation of gettimeofday() for 32 bits processes in a
+ * ppc64 kernel for use in the vDSO
+ *
+ * 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.
+ */
+#include <linux/config.h>
+#include <asm/processor.h>
+#include <asm/ppc_asm.h>
+#include <asm/vdso.h>
+#include <asm/offsets.h>
+#include <asm/unistd.h>
+
+       .text
+/*
+ * Exact prototype of gettimeofday
+ *
+ * int __kernel_gettimeofday(struct timeval *tv, struct timezone *tz);
+ *
+ */
+V_FUNCTION_BEGIN(__kernel_gettimeofday)
+  .cfi_startproc
+       mflr    r12
+  .cfi_register lr,r12
+
+       mr      r10,r3                  /* r10 saves tv */
+       mr      r11,r4                  /* r11 saves tz */
+       bl      __get_datapage@local    /* get data page */
+       mr      r9, r3                  /* datapage ptr in r9 */
+       bl      __do_get_xsec@local     /* get xsec from tb & kernel */
+       bne-    2f                      /* out of line -> do syscall */
+
+       /* seconds are xsec >> 20 */
+       rlwinm  r5,r4,12,20,31
+       rlwimi  r5,r3,12,0,19
+       stw     r5,TVAL32_TV_SEC(r10)
+
+       /* get remaining xsec and convert to usec. we scale
+        * up remaining xsec by 12 bits and get the top 32 bits
+        * of the multiplication
+        */
+       rlwinm  r5,r4,12,0,19
+       lis     r6,1000000@h
+       ori     r6,r6,1000000@l
+       mulhwu  r5,r5,r6
+       stw     r5,TVAL32_TV_USEC(r10)
+
+       cmpli   cr0,r11,0               /* check if tz is NULL */
+       beq     1f
+       lwz     r4,CFG_TZ_MINUTEWEST(r9)/* fill tz */
+       lwz     r5,CFG_TZ_DSTTIME(r9)
+       stw     r4,TZONE_TZ_MINWEST(r11)
+       stw     r5,TZONE_TZ_DSTTIME(r11)
+
+1:     mtlr    r12
+       li      r3,0
+       blr
+
+2:     mr      r3,r10
+       mr      r4,r11
+       li      r0,__NR_gettimeofday
+       sc
+       b       1b
+  .cfi_endproc
+V_FUNCTION_END(__kernel_gettimeofday)
+
+/*
+ * This is the core of gettimeofday(), it returns the xsec
+ * value in r3 & r4 and expects the datapage ptr (non clobbered)
+ * in r9. clobbers r0,r4,r5,r6,r7,r8
+*/
+__do_get_xsec:
+  .cfi_startproc
+       /* Check for update count & load values. We use the low
+        * order 32 bits of the update count
+        */
+1:     lwz     r8,(CFG_TB_UPDATE_COUNT+4)(r9)
+       andi.   r0,r8,1                 /* pending update ? loop */
+       bne-    1b
+       xor     r0,r8,r8                /* create dependency */
+       add     r9,r9,r0
+
+       /* Load orig stamp (offset to TB) */
+       lwz     r5,CFG_TB_ORIG_STAMP(r9)
+       lwz     r6,(CFG_TB_ORIG_STAMP+4)(r9)
+
+       /* Get a stable TB value */
+2:     mftbu   r3
+       mftbl   r4
+       mftbu   r0
+       cmpl    cr0,r3,r0
+       bne-    2b
+
+       /* Substract tb orig stamp. If the high part is non-zero, we jump to the
+        * slow path which call the syscall. If it's ok, then we have our 32 bits
+        * tb_ticks value in r7
+        */
+       subfc   r7,r6,r4
+       subfe.  r0,r5,r3
+       bne-    3f
+
+       /* Load scale factor & do multiplication */
+       lwz     r5,CFG_TB_TO_XS(r9)     /* load values */
+       lwz     r6,(CFG_TB_TO_XS+4)(r9)
+       mulhwu  r4,r7,r5
+       mulhwu  r6,r7,r6
+       mullw   r6,r7,r5
+       addc    r6,r6,r0
+
+       /* At this point, we have the scaled xsec value in r4 + XER:CA
+        * we load & add the stamp since epoch
+        */
+       lwz     r5,CFG_STAMP_XSEC(r9)
+       lwz     r6,(CFG_STAMP_XSEC+4)(r9)
+       adde    r4,r4,r6
+       addze   r3,r5
+
+       /* We now have our result in r3,r4. We create a fake dependency
+        * on that result and re-check the counter
+        */
+       xor     r0,r4,r4
+       add     r9,r9,r0
+       lwz     r0,(CFG_TB_UPDATE_COUNT+4)(r9)
+        cmpl    cr0,r8,r0              /* check if updated */
+       bne-    1b
+
+       /* Warning ! The caller expects CR:EQ to be set to indicate a
+        * successful calculation (so it won't fallback to the syscall
+        * method). We have overriden that CR bit in the counter check,
+        * but fortunately, the loop exit condition _is_ CR:EQ set, so
+        * we can exit safely here. If you change this code, be careful
+        * of that side effect.
+        */
+3:     blr
+  .cfi_endproc
diff --git a/arch/ppc64/kernel/vdso32/note.S b/arch/ppc64/kernel/vdso32/note.S
new file mode 100644 (file)
index 0000000..d4b5be4
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * This supplies .note.* sections to go into the PT_NOTE inside the vDSO text.
+ * Here we can supply some information useful to userland.
+ */
+
+#include <linux/uts.h>
+#include <linux/version.h>
+
+#define ASM_ELF_NOTE_BEGIN(name, flags, vendor, type)                        \
+       .section name, flags;                                                 \
+       .balign 4;                                                            \
+       .long 1f - 0f;          /* name length */                             \
+       .long 3f - 2f;          /* data length */                             \
+       .long type;             /* note type */                               \
+0:     .asciz vendor;          /* vendor name */                             \
+1:     .balign 4;                                                            \
+2:
+
+#define ASM_ELF_NOTE_END                                                     \
+3:     .balign 4;              /* pad out section */                         \
+       .previous
+
+       ASM_ELF_NOTE_BEGIN(".note.kernel-version", "a", UTS_SYSNAME, 0)
+       .long LINUX_VERSION_CODE
+       ASM_ELF_NOTE_END
diff --git a/arch/ppc64/kernel/vdso32/sigtramp.S b/arch/ppc64/kernel/vdso32/sigtramp.S
new file mode 100644 (file)
index 0000000..e046427
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * Signal trampolines for 32 bits processes in a ppc64 kernel for
+ * use in the vDSO
+ *
+ * Copyright (C) 2004 Benjamin Herrenschmuidt (benh@kernel.crashing.org), IBM Corp.
+ * Copyright (C) 2004 Alan Modra (amodra@au.ibm.com)), 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.
+ */
+#include <linux/config.h>
+#include <asm/processor.h>
+#include <asm/ppc_asm.h>
+#include <asm/unistd.h>
+#include <asm/vdso.h>
+
+       .text
+
+/* The nop here is a hack.  The dwarf2 unwind routines subtract 1 from
+   the return address to get an address in the middle of the presumed
+   call instruction.  Since we don't have a call here, we artifically
+   extend the range covered by the unwind info by adding a nop before
+   the real start.  */
+       nop
+V_FUNCTION_BEGIN(__kernel_sigtramp32)
+.Lsig_start = . - 4
+       li      r0,__NR_sigreturn
+       sc
+.Lsig_end:
+V_FUNCTION_END(__kernel_sigtramp32)
+
+.Lsigrt_start:
+       nop
+V_FUNCTION_BEGIN(__kernel_sigtramp_rt32)
+       li      r0,__NR_rt_sigreturn
+       sc
+.Lsigrt_end:
+V_FUNCTION_END(__kernel_sigtramp_rt32)
+
+       .section .eh_frame,"a",@progbits
+
+/* Register r1 can be found at offset 4 of a pt_regs structure.
+   A pointer to the pt_regs is stored in memory at the old sp plus PTREGS.  */
+#define cfa_save \
+  .byte 0x0f;                  /* DW_CFA_def_cfa_expression */         \
+  .uleb128 9f - 1f;            /*   length */                          \
+1:                                                                     \
+  .byte 0x71; .sleb128 PTREGS; /*     DW_OP_breg1 */                   \
+  .byte 0x06;                  /*     DW_OP_deref */                   \
+  .byte 0x23; .uleb128 RSIZE;  /*     DW_OP_plus_uconst */             \
+  .byte 0x06;                  /*     DW_OP_deref */                   \
+9:
+
+/* Register REGNO can be found at offset OFS of a pt_regs structure.
+   A pointer to the pt_regs is stored in memory at the old sp plus PTREGS.  */
+#define rsave(regno, ofs) \
+  .byte 0x10;                  /* DW_CFA_expression */                 \
+  .uleb128 regno;              /*   regno */                           \
+  .uleb128 9f - 1f;            /*   length */                          \
+1:                                                                     \
+  .byte 0x71; .sleb128 PTREGS; /*     DW_OP_breg1 */                   \
+  .byte 0x06;                  /*     DW_OP_deref */                   \
+  .ifne ofs;                                                           \
+    .byte 0x23; .uleb128 ofs;  /*     DW_OP_plus_uconst */             \
+  .endif;                                                              \
+9:
+
+/* If msr bit 1<<25 is set, then VMX register REGNO is at offset REGNO*16
+   of the VMX reg struct.  The VMX reg struct is at offset VREGS of
+   the pt_regs struct.  This macro is for REGNO == 0, and contains
+   'subroutines' that the other macros jump to.  */
+#define vsave_msr0(regno) \
+  .byte 0x10;                  /* DW_CFA_expression */                 \
+  .uleb128 regno + 77;         /*   regno */                           \
+  .uleb128 9f - 1f;            /*   length */                          \
+1:                                                                     \
+  .byte 0x30 + regno;          /*     DW_OP_lit0 */                    \
+2:                                                                     \
+  .byte 0x40;                  /*     DW_OP_lit16 */                   \
+  .byte 0x1e;                  /*     DW_OP_mul */                     \
+3:                                                                     \
+  .byte 0x71; .sleb128 PTREGS; /*     DW_OP_breg1 */                   \
+  .byte 0x06;                  /*     DW_OP_deref */                   \
+  .byte 0x12;                  /*     DW_OP_dup */                     \
+  .byte 0x23;                  /*     DW_OP_plus_uconst */             \
+    .uleb128 33*RSIZE;         /*       msr offset */                  \
+  .byte 0x06;                  /*     DW_OP_deref */                   \
+  .byte 0x0c; .long 1 << 25;   /*     DW_OP_const4u */                 \
+  .byte 0x1a;                  /*     DW_OP_and */                     \
+  .byte 0x12;                  /*     DW_OP_dup, ret 0 if bra taken */ \
+  .byte 0x30;                  /*     DW_OP_lit0 */                    \
+  .byte 0x29;                  /*     DW_OP_eq */                      \
+  .byte 0x28; .short 0x7fff;   /*     DW_OP_bra to end */              \
+  .byte 0x13;                  /*     DW_OP_drop, pop the 0 */         \
+  .byte 0x23; .uleb128 VREGS;  /*     DW_OP_plus_uconst */             \
+  .byte 0x22;                  /*     DW_OP_plus */                    \
+  .byte 0x2f; .short 0x7fff;   /*     DW_OP_skip to end */             \
+9:
+
+/* If msr bit 1<<25 is set, then VMX register REGNO is at offset REGNO*16
+   of the VMX reg struct.  REGNO is 1 thru 31.  */
+#define vsave_msr1(regno) \
+  .byte 0x10;                  /* DW_CFA_expression */                 \
+  .uleb128 regno + 77;         /*   regno */                           \
+  .uleb128 9f - 1f;            /*   length */                          \
+1:                                                                     \
+  .byte 0x30 + regno;          /*     DW_OP_lit n */                   \
+  .byte 0x2f; .short 2b - 9f;  /*     DW_OP_skip */                    \
+9:
+
+/* If msr bit 1<<25 is set, then VMX register REGNO is at offset OFS of
+   the VMX save block.  */
+#define vsave_msr2(regno, ofs) \
+  .byte 0x10;                  /* DW_CFA_expression */                 \
+  .uleb128 regno + 77;         /*   regno */                           \
+  .uleb128 9f - 1f;            /*   length */                          \
+1:                                                                     \
+  .byte 0x0a; .short ofs;      /*     DW_OP_const2u */                 \
+  .byte 0x2f; .short 3b - 9f;  /*     DW_OP_skip */                    \
+9:
+
+/* VMX register REGNO is at offset OFS of the VMX save area.  */
+#define vsave(regno, ofs) \
+  .byte 0x10;                  /* DW_CFA_expression */                 \
+  .uleb128 regno + 77;         /*   regno */                           \
+  .uleb128 9f - 1f;            /*   length */                          \
+1:                                                                     \
+  .byte 0x71; .sleb128 PTREGS; /*     DW_OP_breg1 */                   \
+  .byte 0x06;                  /*     DW_OP_deref */                   \
+  .byte 0x23; .uleb128 VREGS;  /*     DW_OP_plus_uconst */             \
+  .byte 0x23; .uleb128 ofs;    /*     DW_OP_plus_uconst */             \
+9:
+
+/* This is where the pt_regs pointer can be found on the stack.  */
+#define PTREGS 64+28
+
+/* Size of regs.  */
+#define RSIZE 4
+
+/* This is the offset of the VMX regs.  */
+#define VREGS 48*RSIZE+34*8
+
+/* Describe where general purpose regs are saved.  */
+#define EH_FRAME_GEN \
+  cfa_save;                                                            \
+  rsave ( 0,  0*RSIZE);                                                        \
+  rsave ( 2,  2*RSIZE);                                                        \
+  rsave ( 3,  3*RSIZE);                                                        \
+  rsave ( 4,  4*RSIZE);                                                        \
+  rsave ( 5,  5*RSIZE);                                                        \
+  rsave ( 6,  6*RSIZE);                                                        \
+  rsave ( 7,  7*RSIZE);                                                        \
+  rsave ( 8,  8*RSIZE);                                                        \
+  rsave ( 9,  9*RSIZE);                                                        \
+  rsave (10, 10*RSIZE);                                                        \
+  rsave (11, 11*RSIZE);                                                        \
+  rsave (12, 12*RSIZE);                                                        \
+  rsave (13, 13*RSIZE);                                                        \
+  rsave (14, 14*RSIZE);                                                        \
+  rsave (15, 15*RSIZE);                                                        \
+  rsave (16, 16*RSIZE);                                                        \
+  rsave (17, 17*RSIZE);                                                        \
+  rsave (18, 18*RSIZE);                                                        \
+  rsave (19, 19*RSIZE);                                                        \
+  rsave (20, 20*RSIZE);                                                        \
+  rsave (21, 21*RSIZE);                                                        \
+  rsave (22, 22*RSIZE);                                                        \
+  rsave (23, 23*RSIZE);                                                        \
+  rsave (24, 24*RSIZE);                                                        \
+  rsave (25, 25*RSIZE);                                                        \
+  rsave (26, 26*RSIZE);                                                        \
+  rsave (27, 27*RSIZE);                                                        \
+  rsave (28, 28*RSIZE);                                                        \
+  rsave (29, 29*RSIZE);                                                        \
+  rsave (30, 30*RSIZE);                                                        \
+  rsave (31, 31*RSIZE);                                                        \
+  rsave (67, 32*RSIZE);                /* ap, used as temp for nip */          \
+  rsave (65, 36*RSIZE);                /* lr */                                \
+  rsave (70, 38*RSIZE)         /* cr */
+
+/* Describe where the FP regs are saved.  */
+#define EH_FRAME_FP \
+  rsave (32, 48*RSIZE +  0*8);                                         \
+  rsave (33, 48*RSIZE +  1*8);                                         \
+  rsave (34, 48*RSIZE +  2*8);                                         \
+  rsave (35, 48*RSIZE +  3*8);                                         \
+  rsave (36, 48*RSIZE +  4*8);                                         \
+  rsave (37, 48*RSIZE +  5*8);                                         \
+  rsave (38, 48*RSIZE +  6*8);                                         \
+  rsave (39, 48*RSIZE +  7*8);                                         \
+  rsave (40, 48*RSIZE +  8*8);                                         \
+  rsave (41, 48*RSIZE +  9*8);                                         \
+  rsave (42, 48*RSIZE + 10*8);                                         \
+  rsave (43, 48*RSIZE + 11*8);                                         \
+  rsave (44, 48*RSIZE + 12*8);                                         \
+  rsave (45, 48*RSIZE + 13*8);                                         \
+  rsave (46, 48*RSIZE + 14*8);                                         \
+  rsave (47, 48*RSIZE + 15*8);                                         \
+  rsave (48, 48*RSIZE + 16*8);                                         \
+  rsave (49, 48*RSIZE + 17*8);                                         \
+  rsave (50, 48*RSIZE + 18*8);                                         \
+  rsave (51, 48*RSIZE + 19*8);                                         \
+  rsave (52, 48*RSIZE + 20*8);                                         \
+  rsave (53, 48*RSIZE + 21*8);                                         \
+  rsave (54, 48*RSIZE + 22*8);                                         \
+  rsave (55, 48*RSIZE + 23*8);                                         \
+  rsave (56, 48*RSIZE + 24*8);                                         \
+  rsave (57, 48*RSIZE + 25*8);                                         \
+  rsave (58, 48*RSIZE + 26*8);                                         \
+  rsave (59, 48*RSIZE + 27*8);                                         \
+  rsave (60, 48*RSIZE + 28*8);                                         \
+  rsave (61, 48*RSIZE + 29*8);                                         \
+  rsave (62, 48*RSIZE + 30*8);                                         \
+  rsave (63, 48*RSIZE + 31*8)
+
+/* Describe where the VMX regs are saved.  */
+#ifdef CONFIG_ALTIVEC
+#define EH_FRAME_VMX \
+  vsave_msr0 ( 0);                                                     \
+  vsave_msr1 ( 1);                                                     \
+  vsave_msr1 ( 2);                                                     \
+  vsave_msr1 ( 3);                                                     \
+  vsave_msr1 ( 4);                                                     \
+  vsave_msr1 ( 5);                                                     \
+  vsave_msr1 ( 6);                                                     \
+  vsave_msr1 ( 7);                                                     \
+  vsave_msr1 ( 8);                                                     \
+  vsave_msr1 ( 9);                                                     \
+  vsave_msr1 (10);                                                     \
+  vsave_msr1 (11);                                                     \
+  vsave_msr1 (12);                                                     \
+  vsave_msr1 (13);                                                     \
+  vsave_msr1 (14);                                                     \
+  vsave_msr1 (15);                                                     \
+  vsave_msr1 (16);                                                     \
+  vsave_msr1 (17);                                                     \
+  vsave_msr1 (18);                                                     \
+  vsave_msr1 (19);                                                     \
+  vsave_msr1 (20);                                                     \
+  vsave_msr1 (21);                                                     \
+  vsave_msr1 (22);                                                     \
+  vsave_msr1 (23);                                                     \
+  vsave_msr1 (24);                                                     \
+  vsave_msr1 (25);                                                     \
+  vsave_msr1 (26);                                                     \
+  vsave_msr1 (27);                                                     \
+  vsave_msr1 (28);                                                     \
+  vsave_msr1 (29);                                                     \
+  vsave_msr1 (30);                                                     \
+  vsave_msr1 (31);                                                     \
+  vsave_msr2 (33, 32*16+12);                                           \
+  vsave      (32, 32*16)
+#else
+#define EH_FRAME_VMX
+#endif
+
+.Lcie:
+       .long .Lcie_end - .Lcie_start
+.Lcie_start:
+       .long 0                 /* CIE ID */
+       .byte 1                 /* Version number */
+       .string "zR"            /* NUL-terminated augmentation string */
+       .uleb128 4              /* Code alignment factor */
+       .sleb128 -4             /* Data alignment factor */
+       .byte 67                /* Return address register column, ap */
+       .uleb128 1              /* Augmentation value length */
+       .byte 0x1b              /* DW_EH_PE_pcrel | DW_EH_PE_sdata4. */
+       .byte 0x0c,1,0          /* DW_CFA_def_cfa: r1 ofs 0 */
+       .balign 4
+.Lcie_end:
+
+       .long .Lfde0_end - .Lfde0_start
+.Lfde0_start:
+       .long .Lfde0_start - .Lcie      /* CIE pointer. */
+       .long .Lsig_start - .           /* PC start, length */
+       .long .Lsig_end - .Lsig_start
+       .uleb128 0                      /* Augmentation */
+       EH_FRAME_GEN
+       EH_FRAME_FP
+       EH_FRAME_VMX
+       .balign 4
+.Lfde0_end:
+
+/* We have a different stack layout for rt_sigreturn.  */
+#undef PTREGS
+#define PTREGS 64+16+128+20+28
+
+       .long .Lfde1_end - .Lfde1_start
+.Lfde1_start:
+       .long .Lfde1_start - .Lcie      /* CIE pointer. */
+       .long .Lsigrt_start - .         /* PC start, length */
+       .long .Lsigrt_end - .Lsigrt_start
+       .uleb128 0                      /* Augmentation */
+       EH_FRAME_GEN
+       EH_FRAME_FP
+       EH_FRAME_VMX
+       .balign 4
+.Lfde1_end:
diff --git a/arch/ppc64/kernel/vdso32/vdso32.lds.S b/arch/ppc64/kernel/vdso32/vdso32.lds.S
new file mode 100644 (file)
index 0000000..11290c9
--- /dev/null
@@ -0,0 +1,114 @@
+
+/*
+ * This is the infamous ld script for the 32 bits vdso
+ * library
+ */
+#include <asm/vdso.h>
+
+/* Default link addresses for the vDSOs */
+OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc")
+OUTPUT_ARCH(powerpc:common)
+ENTRY(_start)
+
+SECTIONS
+{
+  . = VDSO32_LBASE + SIZEOF_HEADERS;
+  .hash           : { *(.hash) }                       :text
+  .dynsym         : { *(.dynsym) }
+  .dynstr         : { *(.dynstr) }
+  .gnu.version    : { *(.gnu.version) }
+  .gnu.version_d  : { *(.gnu.version_d) }
+  .gnu.version_r  : { *(.gnu.version_r) }
+
+  .note                  : { *(.note.*) }                      :text   :note
+
+  . = ALIGN (16);
+  .text :
+  {
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+  }
+  PROVIDE (__etext = .);
+  PROVIDE (_etext = .);
+  PROVIDE (etext = .);
+
+  /* Other stuff is appended to the text segment: */
+  .rodata              : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+  .rodata1             : { *(.rodata1) }
+
+  .eh_frame_hdr                : { *(.eh_frame_hdr) }          :text   :eh_frame_hdr
+  .eh_frame            : { KEEP (*(.eh_frame)) }       :text
+  .gcc_except_table    : { *(.gcc_except_table) }
+  .fixup               : { *(.fixup) }
+
+  .got ALIGN(4)                : { *(.got.plt) *(.got) }
+
+  .dynamic             : { *(.dynamic) }               :text   :dynamic
+
+  _end = .;
+  __end = .;
+  PROVIDE (end = .);
+
+
+  /* Stabs debugging sections are here too
+   */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+  .debug 0 : { *(.debug) }
+  .line 0 : { *(.line) }
+
+  .debug_srcinfo 0 : { *(.debug_srcinfo) }
+  .debug_sfnames 0 : { *(.debug_sfnames) }
+
+  .debug_aranges 0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+
+  .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev 0 : { *(.debug_abbrev) }
+  .debug_line 0 : { *(.debug_line) }
+  .debug_frame 0 : { *(.debug_frame) }
+  .debug_str 0 : { *(.debug_str) }
+  .debug_loc 0 : { *(.debug_loc) }
+  .debug_macinfo 0 : { *(.debug_macinfo) }
+
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames 0 : { *(.debug_varnames) }
+
+  /DISCARD/ : { *(.note.GNU-stack) }
+  /DISCARD/ : { *(.data .data.* .gnu.linkonce.d.* .sdata*) }
+  /DISCARD/ : { *(.bss .sbss .dynbss .dynsbss) }
+}
+
+
+PHDRS
+{
+  text PT_LOAD FILEHDR PHDRS FLAGS(5); /* PF_R|PF_X */
+  note PT_NOTE FLAGS(4); /* PF_R */
+  dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
+  eh_frame_hdr 0x6474e550; /* PT_GNU_EH_FRAME, but ld doesn't match the name */
+}
+
+
+/*
+ * This controls what symbols we export from the DSO.
+ */
+VERSION
+{
+  VDSO_VERSION_STRING {
+    global:
+       __kernel_datapage_offset; /* Has to be there for the kernel to find it */
+       __kernel_get_syscall_map;
+       __kernel_gettimeofday;
+       __kernel_sync_dicache;
+       __kernel_sync_dicache_p5;
+       __kernel_sigtramp32;
+       __kernel_sigtramp_rt32;
+    local: *;
+  };
+}
diff --git a/arch/ppc64/kernel/vdso32/vdso32_wrapper.S b/arch/ppc64/kernel/vdso32/vdso32_wrapper.S
new file mode 100644 (file)
index 0000000..76ca28e
--- /dev/null
@@ -0,0 +1,13 @@
+#include <linux/init.h>
+#include <asm/page.h>
+
+       .section ".data.page_aligned"
+
+       .globl vdso32_start, vdso32_end
+       .balign PAGE_SIZE
+vdso32_start:
+       .incbin "arch/ppc64/kernel/vdso32/vdso32.so"
+       .balign PAGE_SIZE
+vdso32_end:
+
+       .previous
diff --git a/arch/ppc64/kernel/vdso64/Makefile b/arch/ppc64/kernel/vdso64/Makefile
new file mode 100644 (file)
index 0000000..ab39988
--- /dev/null
@@ -0,0 +1,35 @@
+# List of files in the vdso, has to be asm only for now
+
+obj-vdso64 = sigtramp.o gettimeofday.o datapage.o cacheflush.o note.o
+
+# Build rules
+
+targets := $(obj-vdso64) vdso64.so
+obj-vdso64 := $(addprefix $(obj)/, $(obj-vdso64))
+
+EXTRA_CFLAGS := -shared -s -fno-common -fno-builtin
+EXTRA_CFLAGS +=  -nostdlib -Wl,-soname=linux-vdso64.so.1
+EXTRA_AFLAGS := -D__VDSO64__ -s
+
+obj-y += vdso64_wrapper.o
+extra-y += vdso64.lds
+CPPFLAGS_vdso64.lds += -P -C -U$(ARCH)
+
+# Force dependency (incbin is bad)
+$(obj)/vdso64_wrapper.o : $(obj)/vdso64.so
+
+# link rule for the .so file, .lds has to be first
+$(obj)/vdso64.so: $(src)/vdso64.lds $(obj-vdso64)
+       $(call if_changed,vdso64ld)
+
+# assembly rules for the .S files
+$(obj-vdso64): %.o: %.S
+       $(call if_changed_dep,vdso64as)
+
+# actual build commands
+quiet_cmd_vdso64ld = VDSO64L $@
+      cmd_vdso64ld = $(CC) $(c_flags) -Wl,-T $^ -o $@
+quiet_cmd_vdso64as = VDSO64A $@
+      cmd_vdso64as = $(CC) $(a_flags) -c -o $@ $<
+
+
diff --git a/arch/ppc64/kernel/vdso64/cacheflush.S b/arch/ppc64/kernel/vdso64/cacheflush.S
new file mode 100644 (file)
index 0000000..e0725b7
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * vDSO provided cache flush routines
+ *
+ * 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.
+ */
+#include <linux/config.h>
+#include <asm/processor.h>
+#include <asm/ppc_asm.h>
+#include <asm/vdso.h>
+#include <asm/offsets.h>
+
+       .text
+
+/*
+ * Default "generic" version of __kernel_sync_dicache.
+ *
+ * void __kernel_sync_dicache(unsigned long start, unsigned long end)
+ *
+ * Flushes the data cache & invalidate the instruction cache for the
+ * provided range [start, end[
+ *
+ * Note: all CPUs supported by this kernel have a 128 bytes cache
+ * line size so we don't have to peek that info from the datapage
+ */
+V_FUNCTION_BEGIN(__kernel_sync_dicache)
+  .cfi_startproc
+       li      r5,127
+       andc    r6,r3,r5                /* round low to line bdy */
+       subf    r8,r6,r4                /* compute length */
+       add     r8,r8,r5                /* ensure we get enough */
+       srwi.   r8,r8,7                 /* compute line count */
+       beqlr                           /* nothing to do? */
+       mtctr   r8
+       mr      r3,r6
+1:     dcbst   0,r3
+       addi    r3,r3,128
+       bdnz    1b
+       sync
+       mtctr   r8
+1:     icbi    0,r6
+       addi    r6,r6,128
+       bdnz    1b
+       isync
+       li      r3,0
+       blr
+  .cfi_endproc
+V_FUNCTION_END(__kernel_sync_dicache)
+
+
+/*
+ * POWER5 version of __kernel_sync_dicache
+ */
+V_FUNCTION_BEGIN(__kernel_sync_dicache_p5)
+  .cfi_startproc
+       sync
+       isync
+       li      r3,0
+       blr
+  .cfi_endproc
+V_FUNCTION_END(__kernel_sync_dicache_p5)
diff --git a/arch/ppc64/kernel/vdso64/datapage.S b/arch/ppc64/kernel/vdso64/datapage.S
new file mode 100644 (file)
index 0000000..18afd97
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Access to the shared data page by the vDSO & syscall map
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <asm/processor.h>
+#include <asm/ppc_asm.h>
+#include <asm/offsets.h>
+#include <asm/unistd.h>
+#include <asm/vdso.h>
+
+       .text
+V_FUNCTION_BEGIN(__get_datapage)
+  .cfi_startproc
+       /* We don't want that exposed or overridable as we want other objects
+        * to be able to bl directly to here
+        */
+       .protected __get_datapage
+       .hidden __get_datapage
+
+       mflr    r0
+  .cfi_register lr,r0
+
+       bcl     20,31,1f
+       .global __kernel_datapage_offset;
+__kernel_datapage_offset:
+       .long   0
+1:
+       mflr    r3
+       mtlr    r0
+       lwz     r0,0(r3)
+       add     r3,r0,r3
+       blr
+  .cfi_endproc
+V_FUNCTION_END(__get_datapage)
+
+/*
+ * void *__kernel_get_syscall_map(unsigned int *syscall_count) ;
+ *
+ * returns a pointer to the syscall map. the map is agnostic to the
+ * size of "long", unlike kernel bitops, it stores bits from top to
+ * bottom so that memory actually contains a linear bitmap
+ * check for syscall N by testing bit (0x80000000 >> (N & 0x1f)) of
+ * 32 bits int at N >> 5.
+ */
+V_FUNCTION_BEGIN(__kernel_get_syscall_map)
+  .cfi_startproc
+       mflr    r12
+  .cfi_register lr,r12
+
+       mr      r4,r3
+       bl      V_LOCAL_FUNC(__get_datapage)
+       mtlr    r12
+       addi    r3,r3,CFG_SYSCALL_MAP64
+       cmpli   cr0,r4,0
+       beqlr
+       li      r0,__NR_syscalls
+       stw     r0,0(r4)
+       blr
+  .cfi_endproc
+V_FUNCTION_END(__kernel_get_syscall_map)
diff --git a/arch/ppc64/kernel/vdso64/gettimeofday.S b/arch/ppc64/kernel/vdso64/gettimeofday.S
new file mode 100644 (file)
index 0000000..ed3f970
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Userland implementation of gettimeofday() for 64 bits processes in a
+ * ppc64 kernel for use in the vDSO
+ *
+ * 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.
+ */
+#include <linux/config.h>
+#include <asm/processor.h>
+#include <asm/ppc_asm.h>
+#include <asm/vdso.h>
+#include <asm/offsets.h>
+
+       .text
+/*
+ * Exact prototype of gettimeofday
+ *
+ * int __kernel_gettimeofday(struct timeval *tv, struct timezone *tz);
+ *
+ */
+V_FUNCTION_BEGIN(__kernel_gettimeofday)
+  .cfi_startproc
+       mflr    r12
+  .cfi_register lr,r12
+
+       mr      r11,r3                  /* r11 holds tv */
+       mr      r10,r4                  /* r10 holds tz */
+       bl      V_LOCAL_FUNC(__get_datapage)            /* get data page */
+       bl      V_LOCAL_FUNC(__do_get_xsec)             /* get xsec from tb & kernel */
+       lis     r7,15                   /* r7 = 1000000 = USEC_PER_SEC */
+       ori     r7,r7,16960
+       rldicl  r5,r4,44,20             /* r5 = sec = xsec / XSEC_PER_SEC */
+       rldicr  r6,r5,20,43             /* r6 = sec * XSEC_PER_SEC */
+       std     r5,TVAL64_TV_SEC(r11)   /* store sec in tv */
+       subf    r0,r6,r4                /* r0 = xsec = (xsec - r6) */
+       mulld   r0,r0,r7                /* usec = (xsec * USEC_PER_SEC) / XSEC_PER_SEC */
+       rldicl  r0,r0,44,20
+       cmpldi  cr0,r10,0               /* check if tz is NULL */
+       std     r0,TVAL64_TV_USEC(r11)  /* store usec in tv */
+       beq     1f
+       lwz     r4,CFG_TZ_MINUTEWEST(r3)/* fill tz */
+       lwz     r5,CFG_TZ_DSTTIME(r3)
+       stw     r4,TZONE_TZ_MINWEST(r10)
+       stw     r5,TZONE_TZ_DSTTIME(r10)
+1:     mtlr    r12
+       li      r3,0                    /* always success */
+       blr
+  .cfi_endproc
+V_FUNCTION_END(__kernel_gettimeofday)
+
+
+/*
+ * This is the core of gettimeofday(), it returns the xsec
+ * value in r4 and expects the datapage ptr (non clobbered)
+ * in r3. clobbers r0,r4,r5,r6,r7,r8
+*/
+V_FUNCTION_BEGIN(__do_get_xsec)
+  .cfi_startproc
+       /* check for update count & load values */
+1:     ld      r7,CFG_TB_UPDATE_COUNT(r3)
+       andi.   r0,r4,1                 /* pending update ? loop */
+       bne-    1b
+       xor     r0,r4,r4                /* create dependency */
+       add     r3,r3,r0
+
+       /* Get TB & offset it */
+       mftb    r8
+       ld      r9,CFG_TB_ORIG_STAMP(r3)
+       subf    r8,r9,r8
+
+       /* Scale result */
+       ld      r5,CFG_TB_TO_XS(r3)
+       mulhdu  r8,r8,r5
+
+       /* Add stamp since epoch */
+       ld      r6,CFG_STAMP_XSEC(r3)
+       add     r4,r6,r8
+
+       xor     r0,r4,r4
+       add     r3,r3,r0
+       ld      r0,CFG_TB_UPDATE_COUNT(r3)
+        cmpld   cr0,r0,r7              /* check if updated */
+       bne-    1b
+       blr
+  .cfi_endproc
+V_FUNCTION_END(__do_get_xsec)
diff --git a/arch/ppc64/kernel/vdso64/note.S b/arch/ppc64/kernel/vdso64/note.S
new file mode 100644 (file)
index 0000000..dc2a509
--- /dev/null
@@ -0,0 +1 @@
+#include "../vdso32/note.S"
diff --git a/arch/ppc64/kernel/vdso64/sigtramp.S b/arch/ppc64/kernel/vdso64/sigtramp.S
new file mode 100644 (file)
index 0000000..8ae8f20
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Signal trampoline for 64 bits processes in a ppc64 kernel for
+ * use in the vDSO
+ *
+ * Copyright (C) 2004 Benjamin Herrenschmuidt (benh@kernel.crashing.org), IBM Corp.
+ * Copyright (C) 2004 Alan Modra (amodra@au.ibm.com)), 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.
+ */
+#include <linux/config.h>
+#include <asm/processor.h>
+#include <asm/ppc_asm.h>
+#include <asm/unistd.h>
+#include <asm/vdso.h>
+
+       .text
+
+/* The nop here is a hack.  The dwarf2 unwind routines subtract 1 from
+   the return address to get an address in the middle of the presumed
+   call instruction.  Since we don't have a call here, we artifically
+   extend the range covered by the unwind info by padding before the
+   real start.  */
+       nop
+       .balign 8
+V_FUNCTION_BEGIN(__kernel_sigtramp_rt64)
+.Lsigrt_start = . - 4
+       addi    r1, r1, __SIGNAL_FRAMESIZE
+       li      r0,__NR_rt_sigreturn
+       sc
+.Lsigrt_end:
+V_FUNCTION_END(__kernel_sigtramp_rt64)
+/* The ".balign 8" above and the following zeros mimic the old stack
+   trampoline layout.  The last magic value is the ucontext pointer,
+   chosen in such a way that older libgcc unwind code returns a zero
+   for a sigcontext pointer.  */
+       .long 0,0,0
+       .quad 0,-21*8
+
+/* Register r1 can be found at offset 8 of a pt_regs structure.
+   A pointer to the pt_regs is stored in memory at the old sp plus PTREGS.  */
+#define cfa_save \
+  .byte 0x0f;                  /* DW_CFA_def_cfa_expression */         \
+  .uleb128 9f - 1f;            /*   length */                          \
+1:                                                                     \
+  .byte 0x71; .sleb128 PTREGS; /*     DW_OP_breg1 */                   \
+  .byte 0x06;                  /*     DW_OP_deref */                   \
+  .byte 0x23; .uleb128 RSIZE;  /*     DW_OP_plus_uconst */             \
+  .byte 0x06;                  /*     DW_OP_deref */                   \
+9:
+
+/* Register REGNO can be found at offset OFS of a pt_regs structure.
+   A pointer to the pt_regs is stored in memory at the old sp plus PTREGS.  */
+#define rsave(regno, ofs) \
+  .byte 0x10;                  /* DW_CFA_expression */                 \
+  .uleb128 regno;              /*   regno */                           \
+  .uleb128 9f - 1f;            /*   length */                          \
+1:                                                                     \
+  .byte 0x71; .sleb128 PTREGS; /*     DW_OP_breg1 */                   \
+  .byte 0x06;                  /*     DW_OP_deref */                   \
+  .ifne ofs;                                                           \
+    .byte 0x23; .uleb128 ofs;  /*     DW_OP_plus_uconst */             \
+  .endif;                                                              \
+9:
+
+/* If msr bit 1<<25 is set, then VMX register REGNO is at offset REGNO*16
+   of the VMX reg struct.  A pointer to the VMX reg struct is at VREGS in
+   the pt_regs struct.  This macro is for REGNO == 0, and contains
+   'subroutines' that the other macros jump to.  */
+#define vsave_msr0(regno) \
+  .byte 0x10;                  /* DW_CFA_expression */                 \
+  .uleb128 regno + 77;         /*   regno */                           \
+  .uleb128 9f - 1f;            /*   length */                          \
+1:                                                                     \
+  .byte 0x30 + regno;          /*     DW_OP_lit0 */                    \
+2:                                                                     \
+  .byte 0x40;                  /*     DW_OP_lit16 */                   \
+  .byte 0x1e;                  /*     DW_OP_mul */                     \
+3:                                                                     \
+  .byte 0x71; .sleb128 PTREGS; /*     DW_OP_breg1 */                   \
+  .byte 0x06;                  /*     DW_OP_deref */                   \
+  .byte 0x12;                  /*     DW_OP_dup */                     \
+  .byte 0x23;                  /*     DW_OP_plus_uconst */             \
+    .uleb128 33*RSIZE;         /*       msr offset */                  \
+  .byte 0x06;                  /*     DW_OP_deref */                   \
+  .byte 0x0c; .long 1 << 25;   /*     DW_OP_const4u */                 \
+  .byte 0x1a;                  /*     DW_OP_and */                     \
+  .byte 0x12;                  /*     DW_OP_dup, ret 0 if bra taken */ \
+  .byte 0x30;                  /*     DW_OP_lit0 */                    \
+  .byte 0x29;                  /*     DW_OP_eq */                      \
+  .byte 0x28; .short 0x7fff;   /*     DW_OP_bra to end */              \
+  .byte 0x13;                  /*     DW_OP_drop, pop the 0 */         \
+  .byte 0x23; .uleb128 VREGS;  /*     DW_OP_plus_uconst */             \
+  .byte 0x06;                  /*     DW_OP_deref */                   \
+  .byte 0x22;                  /*     DW_OP_plus */                    \
+  .byte 0x2f; .short 0x7fff;   /*     DW_OP_skip to end */             \
+9:
+
+/* If msr bit 1<<25 is set, then VMX register REGNO is at offset REGNO*16
+   of the VMX reg struct.  REGNO is 1 thru 31.  */
+#define vsave_msr1(regno) \
+  .byte 0x10;                  /* DW_CFA_expression */                 \
+  .uleb128 regno + 77;         /*   regno */                           \
+  .uleb128 9f - 1f;            /*   length */                          \
+1:                                                                     \
+  .byte 0x30 + regno;          /*     DW_OP_lit n */                   \
+  .byte 0x2f; .short 2b - 9f;  /*     DW_OP_skip */                    \
+9:
+
+/* If msr bit 1<<25 is set, then VMX register REGNO is at offset OFS of
+   the VMX save block.  */
+#define vsave_msr2(regno, ofs) \
+  .byte 0x10;                  /* DW_CFA_expression */                 \
+  .uleb128 regno + 77;         /*   regno */                           \
+  .uleb128 9f - 1f;            /*   length */                          \
+1:                                                                     \
+  .byte 0x0a; .short ofs;      /*     DW_OP_const2u */                 \
+  .byte 0x2f; .short 3b - 9f;  /*     DW_OP_skip */                    \
+9:
+
+/* VMX register REGNO is at offset OFS of the VMX save area.  */
+#define vsave(regno, ofs) \
+  .byte 0x10;                  /* DW_CFA_expression */                 \
+  .uleb128 regno + 77;         /*   regno */                           \
+  .uleb128 9f - 1f;            /*   length */                          \
+1:                                                                     \
+  .byte 0x71; .sleb128 PTREGS; /*     DW_OP_breg1 */                   \
+  .byte 0x06;                  /*     DW_OP_deref */                   \
+  .byte 0x23; .uleb128 VREGS;  /*     DW_OP_plus_uconst */             \
+  .byte 0x06;                  /*     DW_OP_deref */                   \
+  .byte 0x23; .uleb128 ofs;    /*     DW_OP_plus_uconst */             \
+9:
+
+/* This is where the pt_regs pointer can be found on the stack.  */
+#define PTREGS 128+168+56
+
+/* Size of regs.  */
+#define RSIZE 8
+
+/* This is the offset of the VMX reg pointer.  */
+#define VREGS 48*RSIZE+33*8
+
+/* Describe where general purpose regs are saved.  */
+#define EH_FRAME_GEN \
+  cfa_save;                                                            \
+  rsave ( 0,  0*RSIZE);                                                        \
+  rsave ( 2,  2*RSIZE);                                                        \
+  rsave ( 3,  3*RSIZE);                                                        \
+  rsave ( 4,  4*RSIZE);                                                        \
+  rsave ( 5,  5*RSIZE);                                                        \
+  rsave ( 6,  6*RSIZE);                                                        \
+  rsave ( 7,  7*RSIZE);                                                        \
+  rsave ( 8,  8*RSIZE);                                                        \
+  rsave ( 9,  9*RSIZE);                                                        \
+  rsave (10, 10*RSIZE);                                                        \
+  rsave (11, 11*RSIZE);                                                        \
+  rsave (12, 12*RSIZE);                                                        \
+  rsave (13, 13*RSIZE);                                                        \
+  rsave (14, 14*RSIZE);                                                        \
+  rsave (15, 15*RSIZE);                                                        \
+  rsave (16, 16*RSIZE);                                                        \
+  rsave (17, 17*RSIZE);                                                        \
+  rsave (18, 18*RSIZE);                                                        \
+  rsave (19, 19*RSIZE);                                                        \
+  rsave (20, 20*RSIZE);                                                        \
+  rsave (21, 21*RSIZE);                                                        \
+  rsave (22, 22*RSIZE);                                                        \
+  rsave (23, 23*RSIZE);                                                        \
+  rsave (24, 24*RSIZE);                                                        \
+  rsave (25, 25*RSIZE);                                                        \
+  rsave (26, 26*RSIZE);                                                        \
+  rsave (27, 27*RSIZE);                                                        \
+  rsave (28, 28*RSIZE);                                                        \
+  rsave (29, 29*RSIZE);                                                        \
+  rsave (30, 30*RSIZE);                                                        \
+  rsave (31, 31*RSIZE);                                                        \
+  rsave (67, 32*RSIZE);                /* ap, used as temp for nip */          \
+  rsave (65, 36*RSIZE);                /* lr */                                \
+  rsave (70, 38*RSIZE)         /* cr */
+
+/* Describe where the FP regs are saved.  */
+#define EH_FRAME_FP \
+  rsave (32, 48*RSIZE +  0*8);                                         \
+  rsave (33, 48*RSIZE +  1*8);                                         \
+  rsave (34, 48*RSIZE +  2*8);                                         \
+  rsave (35, 48*RSIZE +  3*8);                                         \
+  rsave (36, 48*RSIZE +  4*8);                                         \
+  rsave (37, 48*RSIZE +  5*8);                                         \
+  rsave (38, 48*RSIZE +  6*8);                                         \
+  rsave (39, 48*RSIZE +  7*8);                                         \
+  rsave (40, 48*RSIZE +  8*8);                                         \
+  rsave (41, 48*RSIZE +  9*8);                                         \
+  rsave (42, 48*RSIZE + 10*8);                                         \
+  rsave (43, 48*RSIZE + 11*8);                                         \
+  rsave (44, 48*RSIZE + 12*8);                                         \
+  rsave (45, 48*RSIZE + 13*8);                                         \
+  rsave (46, 48*RSIZE + 14*8);                                         \
+  rsave (47, 48*RSIZE + 15*8);                                         \
+  rsave (48, 48*RSIZE + 16*8);                                         \
+  rsave (49, 48*RSIZE + 17*8);                                         \
+  rsave (50, 48*RSIZE + 18*8);                                         \
+  rsave (51, 48*RSIZE + 19*8);                                         \
+  rsave (52, 48*RSIZE + 20*8);                                         \
+  rsave (53, 48*RSIZE + 21*8);                                         \
+  rsave (54, 48*RSIZE + 22*8);                                         \
+  rsave (55, 48*RSIZE + 23*8);                                         \
+  rsave (56, 48*RSIZE + 24*8);                                         \
+  rsave (57, 48*RSIZE + 25*8);                                         \
+  rsave (58, 48*RSIZE + 26*8);                                         \
+  rsave (59, 48*RSIZE + 27*8);                                         \
+  rsave (60, 48*RSIZE + 28*8);                                         \
+  rsave (61, 48*RSIZE + 29*8);                                         \
+  rsave (62, 48*RSIZE + 30*8);                                         \
+  rsave (63, 48*RSIZE + 31*8)
+
+/* Describe where the VMX regs are saved.  */
+#ifdef CONFIG_ALTIVEC
+#define EH_FRAME_VMX \
+  vsave_msr0 ( 0);                                                     \
+  vsave_msr1 ( 1);                                                     \
+  vsave_msr1 ( 2);                                                     \
+  vsave_msr1 ( 3);                                                     \
+  vsave_msr1 ( 4);                                                     \
+  vsave_msr1 ( 5);                                                     \
+  vsave_msr1 ( 6);                                                     \
+  vsave_msr1 ( 7);                                                     \
+  vsave_msr1 ( 8);                                                     \
+  vsave_msr1 ( 9);                                                     \
+  vsave_msr1 (10);                                                     \
+  vsave_msr1 (11);                                                     \
+  vsave_msr1 (12);                                                     \
+  vsave_msr1 (13);                                                     \
+  vsave_msr1 (14);                                                     \
+  vsave_msr1 (15);                                                     \
+  vsave_msr1 (16);                                                     \
+  vsave_msr1 (17);                                                     \
+  vsave_msr1 (18);                                                     \
+  vsave_msr1 (19);                                                     \
+  vsave_msr1 (20);                                                     \
+  vsave_msr1 (21);                                                     \
+  vsave_msr1 (22);                                                     \
+  vsave_msr1 (23);                                                     \
+  vsave_msr1 (24);                                                     \
+  vsave_msr1 (25);                                                     \
+  vsave_msr1 (26);                                                     \
+  vsave_msr1 (27);                                                     \
+  vsave_msr1 (28);                                                     \
+  vsave_msr1 (29);                                                     \
+  vsave_msr1 (30);                                                     \
+  vsave_msr1 (31);                                                     \
+  vsave_msr2 (33, 32*16+12);                                           \
+  vsave      (32, 33*16)
+#else
+#define EH_FRAME_VMX
+#endif
+
+       .section .eh_frame,"a",@progbits
+.Lcie:
+       .long .Lcie_end - .Lcie_start
+.Lcie_start:
+       .long 0                 /* CIE ID */
+       .byte 1                 /* Version number */
+       .string "zR"            /* NUL-terminated augmentation string */
+       .uleb128 4              /* Code alignment factor */
+       .sleb128 -8             /* Data alignment factor */
+       .byte 67                /* Return address register column, ap */
+       .uleb128 1              /* Augmentation value length */
+       .byte 0x14              /* DW_EH_PE_pcrel | DW_EH_PE_udata8. */
+       .byte 0x0c,1,0          /* DW_CFA_def_cfa: r1 ofs 0 */
+       .balign 8
+.Lcie_end:
+
+       .long .Lfde0_end - .Lfde0_start
+.Lfde0_start:
+       .long .Lfde0_start - .Lcie      /* CIE pointer. */
+       .quad .Lsigrt_start - .         /* PC start, length */
+       .quad .Lsigrt_end - .Lsigrt_start
+       .uleb128 0                      /* Augmentation */
+       EH_FRAME_GEN
+       EH_FRAME_FP
+       EH_FRAME_VMX
+# Do we really need to describe the frame at this point?  ie. will
+# we ever have some call chain that returns somewhere past the addi?
+# I don't think so, since gcc doesn't support async signals.
+#      .byte 0x41              /* DW_CFA_advance_loc 1*4 */
+#undef PTREGS
+#define PTREGS 168+56
+#      EH_FRAME_GEN
+#      EH_FRAME_FP
+#      EH_FRAME_VMX
+       .balign 8
+.Lfde0_end:
diff --git a/arch/ppc64/kernel/vdso64/vdso64.lds.S b/arch/ppc64/kernel/vdso64/vdso64.lds.S
new file mode 100644 (file)
index 0000000..9cb2818
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * This is the infamous ld script for the 64 bits vdso
+ * library
+ */
+#include <asm/vdso.h>
+
+OUTPUT_FORMAT("elf64-powerpc", "elf64-powerpc", "elf64-powerpc")
+OUTPUT_ARCH(powerpc:common64)
+ENTRY(_start)
+
+SECTIONS
+{
+  . = VDSO64_LBASE + SIZEOF_HEADERS;
+  .hash           : { *(.hash) }               :text
+  .dynsym         : { *(.dynsym) }
+  .dynstr         : { *(.dynstr) }
+  .gnu.version    : { *(.gnu.version) }
+  .gnu.version_d  : { *(.gnu.version_d) }
+  .gnu.version_r  : { *(.gnu.version_r) }
+
+  .note                  : { *(.note.*) }              :text   :note
+
+  . = ALIGN (16);
+  .text           :
+  {
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    *(.sfpr .glink)
+  }                                            :text
+  PROVIDE (__etext = .);
+  PROVIDE (_etext = .);
+  PROVIDE (etext = .);
+
+  /* Other stuff is appended to the text segment: */
+  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+  .rodata1        : { *(.rodata1) }
+  .eh_frame_hdr   : { *(.eh_frame_hdr) }       :text   :eh_frame_hdr
+  .eh_frame       : { KEEP (*(.eh_frame)) }    :text
+  .gcc_except_table   : { *(.gcc_except_table) }
+
+  .opd           ALIGN(8) : { KEEP (*(.opd)) }
+  .got          ALIGN(8) : { *(.got .toc) }
+  .rela.dyn     ALIGN(8) : { *(.rela.dyn) }
+
+  .dynamic        : { *(.dynamic) }            :text   :dynamic
+
+  _end = .;
+  PROVIDE (end = .);
+
+  /* Stabs debugging sections are here too
+   */
+  .stab          0 : { *(.stab) }
+  .stabstr       0 : { *(.stabstr) }
+  .stab.excl     0 : { *(.stab.excl) }
+  .stab.exclstr  0 : { *(.stab.exclstr) }
+  .stab.index    0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment       0 : { *(.comment) }
+  /* DWARF debug sectio/ns.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+
+  /DISCARD/ : { *(.note.GNU-stack) }
+  /DISCARD/ : { *(.branch_lt) }
+  /DISCARD/ : { *(.data .data.* .gnu.linkonce.d.*) }
+  /DISCARD/ : { *(.bss .sbss .dynbss .dynsbss) }
+}
+
+PHDRS
+{
+  text PT_LOAD FILEHDR PHDRS FLAGS(5); /* PF_R|PF_X */
+  note PT_NOTE FLAGS(4); /* PF_R */
+  dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
+  eh_frame_hdr 0x6474e550; /* PT_GNU_EH_FRAME, but ld doesn't match the name */
+}
+
+/*
+ * This controls what symbols we export from the DSO.
+ */
+VERSION
+{
+  VDSO_VERSION_STRING {
+    global:
+       __kernel_datapage_offset; /* Has to be there for the kernel to find it */
+       __kernel_get_syscall_map;
+       __kernel_gettimeofday;
+       __kernel_sync_dicache;
+       __kernel_sync_dicache_p5;
+       __kernel_sigtramp_rt64;
+    local: *;
+  };
+}
diff --git a/arch/ppc64/kernel/vdso64/vdso64_wrapper.S b/arch/ppc64/kernel/vdso64/vdso64_wrapper.S
new file mode 100644 (file)
index 0000000..771c274
--- /dev/null
@@ -0,0 +1,13 @@
+#include <linux/init.h>
+#include <asm/page.h>
+
+       .section ".data.page_aligned"
+
+       .globl vdso64_start, vdso64_end
+       .balign PAGE_SIZE
+vdso64_start:
+       .incbin "arch/ppc64/kernel/vdso64/vdso64.so"
+       .balign PAGE_SIZE
+vdso64_end:
+
+       .previous
diff --git a/arch/sh/boards/hp6xx/hp620/setup.c b/arch/sh/boards/hp6xx/hp620/setup.c
new file mode 100644 (file)
index 0000000..045fc5d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * linux/arch/sh/boards/hp6xx/hp620/setup.c
+ *
+ * Copyright (C) 2002 Andriy Skulysh, 2005 Kristoffer Ericson
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See Linux/COPYING for more information.
+ *
+ * Setup code for an HP620.
+ * Due to similiarity with hp680/hp690 same inits are done (for now)
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <asm/hd64461/hd64461.h>
+#include <asm/io.h>
+#include <asm/hp6xx/hp6xx.h>
+#include <asm/cpu/dac.h>
+
+const char *get_system_type(void)
+{
+       return "HP620";
+}
+
+int __init platform_setup(void)
+{
+       u16 v;
+
+       v  = inw(HD64461_STBCR);
+       v |= HD64461_STBCR_SURTST | HD64461_STBCR_SIRST  |
+            HD64461_STBCR_STM1ST | HD64461_STBCR_STM0ST |
+            HD64461_STBCR_SAFEST | HD64461_STBCR_SPC0ST |
+            HD64461_STBCR_SMIAST | HD64461_STBCR_SAFECKE_OST |
+            HD64461_STBCR_SAFECKE_IST;
+       outw(v, HD64461_STBCR);
+
+       v  = inw(HD64461_GPADR);
+       v |= HD64461_GPADR_SPEAKER | HD64461_GPADR_PCMCIA0;
+       outw(v, HD64461_GPADR);
+
+       sh_dac_disable(DAC_SPEAKER_VOLUME);
+
+       return 0;
+}
+
diff --git a/arch/sh/configs/se7750_defconfig b/arch/sh/configs/se7750_defconfig
new file mode 100644 (file)
index 0000000..6dc3158
--- /dev/null
@@ -0,0 +1,713 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.11-sh
+# Wed Mar  2 15:09:46 2005
+#
+CONFIG_SUPERH=y
+CONFIG_UID16=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_CALIBRATE_DELAY=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=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=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 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_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_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 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=y
+# 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_MMU=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttySC1,38400 root=/dev/nfs ip=bootp"
+CONFIG_MEMORY_START=0x0c000000
+CONFIG_MEMORY_SIZE=0x02000000
+CONFIG_MEMORY_SET=y
+# CONFIG_MEMORY_OVERRIDE is not set
+CONFIG_CF_ENABLER=y
+# CONFIG_CF_AREA5 is not set
+CONFIG_CF_AREA6=y
+CONFIG_CF_BASE_ADDR=0xb8000000
+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 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=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_PCI is not set
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# PC-card bridges
+#
+
+#
+# PCI Hotplug Support
+#
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_FLAT is not set
+# 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=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=y
+# 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=0x00010000
+# 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
+# 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
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT 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 is not set
+# CONFIG_BLK_DEV_NBD 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
+#
+
+#
+# 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=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
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MII is not set
+CONFIG_STNIC=y
+# CONFIG_SMC91X is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+
+#
+# Ethernet (10000 Mbit)
+#
+
+#
+# Token Ring devices
+#
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+
+#
+# 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=y
+# CONFIG_SERIAL_8250_CONSOLE is not set
+CONFIG_SERIAL_8250_NR_UARTS=2
+# 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
+
+#
+# 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=y
+# 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_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_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
+
+#
+# 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
+
+#
+# XFS support
+#
+# 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_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_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 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_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
+
+#
+# Profiling support
+#
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_FRAME_POINTER is not set
+# CONFIG_SH_STANDARD_BIOS is not set
+# CONFIG_EARLY_SCIF_CONSOLE is not set
+# CONFIG_KGDB 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
+#
+
+#
+# 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/sh64/lib/iomap.c b/arch/sh64/lib/iomap.c
new file mode 100644 (file)
index 0000000..83c5f0c
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * arch/sh64/lib/iomap.c
+ *
+ * Generic sh64 iomap interface
+ *
+ * Copyright (C) 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/config.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+void __iomem *__attribute__ ((weak))
+ioport_map(unsigned long port, unsigned int len)
+{
+       return (void __iomem *)port;
+}
+
+void ioport_unmap(void __iomem *addr)
+{
+       /* Nothing .. */
+}
+
+void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max)
+{
+       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)
+               return NULL;
+       if (max && len > max)
+               len = max;
+       if (flags & IORESOURCE_IO)
+               return ioport_map(start + pciio_virt, len);
+       if (flags & IORESOURCE_MEM)
+               return (void __iomem *)start;
+
+       /* What? */
+       return NULL;
+}
+
+void pci_iounmap(struct pci_dev *dev, void __iomem *addr)
+{
+       /* Nothing .. */
+}
+
+EXPORT_SYMBOL(ioport_map);
+EXPORT_SYMBOL(ioport_unmap);
+EXPORT_SYMBOL(pci_iomap);
+EXPORT_SYMBOL(pci_iounmap);
+
diff --git a/arch/sh64/mach-cayman/iomap.c b/arch/sh64/mach-cayman/iomap.c
new file mode 100644 (file)
index 0000000..d6a538c
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * arch/sh64/mach-cayman/iomap.c
+ *
+ * Cayman iomap interface
+ *
+ * Copyright (C) 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/config.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/cayman.h>
+
+void __iomem *ioport_map(unsigned long port, unsigned int len)
+{
+       if (port < 0x400)
+               return (void __iomem *)((port << 2) | smsc_superio_virt);
+
+       return (void __iomem *)port;
+}
+
diff --git a/arch/sparc64/lib/bzero.S b/arch/sparc64/lib/bzero.S
new file mode 100644 (file)
index 0000000..21a933f
--- /dev/null
@@ -0,0 +1,158 @@
+/* bzero.S: Simple prefetching memset, bzero, and clear_user
+ *          implementations.
+ *
+ * Copyright (C) 2005 David S. Miller <davem@davemloft.net>
+ */
+
+       .text
+
+       .globl  __memset
+       .type   __memset, #function
+__memset:              /* %o0=buf, %o1=pat, %o2=len */
+
+       .globl  memset
+       .type   memset, #function
+memset:                        /* %o0=buf, %o1=pat, %o2=len */
+       and             %o1, 0xff, %o3
+       mov             %o2, %o1
+       sllx            %o3, 8, %g1
+       or              %g1, %o3, %o2
+       sllx            %o2, 16, %g1
+       or              %g1, %o2, %o2
+       sllx            %o2, 32, %g1
+       ba,pt           %xcc, 1f
+        or             %g1, %o2, %o2
+
+       .globl  __bzero
+       .type   __bzero, #function
+__bzero:               /* %o0=buf, %o1=len */
+       clr             %o2
+1:     mov             %o0, %o3
+       brz,pn          %o1, __bzero_done
+        cmp            %o1, 16
+       bl,pn           %icc, __bzero_tiny
+        prefetch       [%o0 + 0x000], #n_writes
+       andcc           %o0, 0x3, %g0
+       be,pt           %icc, 2f
+1:      stb            %o2, [%o0 + 0x00]
+       add             %o0, 1, %o0
+       andcc           %o0, 0x3, %g0
+       bne,pn          %icc, 1b
+        sub            %o1, 1, %o1
+2:     andcc           %o0, 0x7, %g0
+       be,pt           %icc, 3f
+        stw            %o2, [%o0 + 0x00]
+       sub             %o1, 4, %o1
+       add             %o0, 4, %o0
+3:     and             %o1, 0x38, %g1
+       cmp             %o1, 0x40
+       andn            %o1, 0x3f, %o4
+       bl,pn           %icc, 5f
+        and            %o1, 0x7, %o1
+       prefetch        [%o0 + 0x040], #n_writes
+       prefetch        [%o0 + 0x080], #n_writes
+       prefetch        [%o0 + 0x0c0], #n_writes
+       prefetch        [%o0 + 0x100], #n_writes
+       prefetch        [%o0 + 0x140], #n_writes
+4:     prefetch        [%o0 + 0x180], #n_writes
+       stx             %o2, [%o0 + 0x00]
+       stx             %o2, [%o0 + 0x08]
+       stx             %o2, [%o0 + 0x10]
+       stx             %o2, [%o0 + 0x18]
+       stx             %o2, [%o0 + 0x20]
+       stx             %o2, [%o0 + 0x28]
+       stx             %o2, [%o0 + 0x30]
+       stx             %o2, [%o0 + 0x38]
+       subcc           %o4, 0x40, %o4
+       bne,pt          %icc, 4b
+        add            %o0, 0x40, %o0
+       brz,pn          %g1, 6f
+        nop
+5:     stx             %o2, [%o0 + 0x00]
+       subcc           %g1, 8, %g1
+       bne,pt          %icc, 5b
+        add            %o0, 0x8, %o0
+6:     brz,pt          %o1, __bzero_done
+        nop
+__bzero_tiny:
+1:     stb             %o2, [%o0 + 0x00]
+       subcc           %o1, 1, %o1
+       bne,pt          %icc, 1b
+        add            %o0, 1, %o0
+__bzero_done:
+       retl
+        mov            %o3, %o0
+       .size           __bzero, .-__bzero
+       .size           __memset, .-__memset
+       .size           memset, .-memset
+
+#define EX_ST(x,y)             \
+98:    x,y;                    \
+       .section .fixup;        \
+       .align 4;               \
+99:    retl;                   \
+        mov    %o1, %o0;       \
+       .section __ex_table;    \
+       .align 4;               \
+       .word 98b, 99b;         \
+       .text;                  \
+       .align 4;
+
+       .globl  __bzero_noasi
+       .type   __bzero_noasi, #function
+__bzero_noasi:         /* %o0=buf, %o1=len */
+       brz,pn          %o1, __bzero_noasi_done
+        cmp            %o1, 16
+       bl,pn           %icc, __bzero_noasi_tiny
+        EX_ST(prefetcha [%o0 + 0x00] %asi, #n_writes)
+       andcc           %o0, 0x3, %g0
+       be,pt           %icc, 2f
+1:      EX_ST(stba     %g0, [%o0 + 0x00] %asi)
+       add             %o0, 1, %o0
+       andcc           %o0, 0x3, %g0
+       bne,pn          %icc, 1b
+        sub            %o1, 1, %o1
+2:     andcc           %o0, 0x7, %g0
+       be,pt           %icc, 3f
+        EX_ST(stwa     %g0, [%o0 + 0x00] %asi)
+       sub             %o1, 4, %o1
+       add             %o0, 4, %o0
+3:     and             %o1, 0x38, %g1
+       cmp             %o1, 0x40
+       andn            %o1, 0x3f, %o4
+       bl,pn           %icc, 5f
+        and            %o1, 0x7, %o1
+       EX_ST(prefetcha [%o0 + 0x040] %asi, #n_writes)
+       EX_ST(prefetcha [%o0 + 0x080] %asi, #n_writes)
+       EX_ST(prefetcha [%o0 + 0x0c0] %asi, #n_writes)
+       EX_ST(prefetcha [%o0 + 0x100] %asi, #n_writes)
+       EX_ST(prefetcha [%o0 + 0x140] %asi, #n_writes)
+4:     EX_ST(prefetcha [%o0 + 0x180] %asi, #n_writes)
+       EX_ST(stxa      %g0, [%o0 + 0x00] %asi)
+       EX_ST(stxa      %g0, [%o0 + 0x08] %asi)
+       EX_ST(stxa      %g0, [%o0 + 0x10] %asi)
+       EX_ST(stxa      %g0, [%o0 + 0x18] %asi)
+       EX_ST(stxa      %g0, [%o0 + 0x20] %asi)
+       EX_ST(stxa      %g0, [%o0 + 0x28] %asi)
+       EX_ST(stxa      %g0, [%o0 + 0x30] %asi)
+       EX_ST(stxa      %g0, [%o0 + 0x38] %asi)
+       subcc           %o4, 0x40, %o4
+       bne,pt          %icc, 4b
+        add            %o0, 0x40, %o0
+       brz,pn          %g1, 6f
+        nop
+5:     EX_ST(stxa      %g0, [%o0 + 0x00] %asi)
+       subcc           %g1, 8, %g1
+       bne,pt          %icc, 5b
+        add            %o0, 0x8, %o0
+6:     brz,pt          %o1, __bzero_noasi_done
+        nop
+__bzero_noasi_tiny:
+1:     EX_ST(stba      %g0, [%o0 + 0x00] %asi)
+       subcc           %o1, 1, %o1
+       bne,pt          %icc, 1b
+        add            %o0, 1, %o0
+__bzero_noasi_done:
+       retl
+        clr            %o0
+       .size           __bzero_noasi, .-__bzero_noasi
diff --git a/arch/um/drivers/random.c b/arch/um/drivers/random.c
new file mode 100644 (file)
index 0000000..f9e2219
--- /dev/null
@@ -0,0 +1,128 @@
+/* Copyright (C) 2005 Jeff Dike <jdike@addtoit.com> */
+/* Much of this ripped from drivers/char/hw_random.c, see there for other
+ * copyright.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include "os.h"
+
+/*
+ * core module and version information
+ */
+#define RNG_VERSION "1.0.0"
+#define RNG_MODULE_NAME "random"
+
+#define RNG_MISCDEV_MINOR              183 /* official */
+
+static int random_fd = -1;
+
+static int rng_dev_open (struct inode *inode, struct file *filp)
+{
+       /* enforce read-only access to this chrdev */
+       if ((filp->f_mode & FMODE_READ) == 0)
+               return -EINVAL;
+       if (filp->f_mode & FMODE_WRITE)
+               return -EINVAL;
+
+       return 0;
+}
+
+static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size,
+                             loff_t * offp)
+{
+        u32 data;
+        int n, ret = 0, have_data;
+
+        while(size){
+                n = os_read_file(random_fd, &data, sizeof(data));
+                if(n > 0){
+                        have_data = n;
+                        while (have_data && size) {
+                                if (put_user((u8)data, buf++)) {
+                                        ret = ret ? : -EFAULT;
+                                        break;
+                                }
+                                size--;
+                                ret++;
+                                have_data--;
+                                data>>=8;
+                        }
+                }
+                else if(n == -EAGAIN){
+                        if (filp->f_flags & O_NONBLOCK)
+                                return ret ? : -EAGAIN;
+
+                        if(need_resched()){
+                                current->state = TASK_INTERRUPTIBLE;
+                                schedule_timeout(1);
+                        }
+                }
+                else return n;
+               if (signal_pending (current))
+                       return ret ? : -ERESTARTSYS;
+       }
+       return ret;
+}
+
+static struct file_operations rng_chrdev_ops = {
+       .owner          = THIS_MODULE,
+       .open           = rng_dev_open,
+       .read           = rng_dev_read,
+};
+
+static struct miscdevice rng_miscdev = {
+       RNG_MISCDEV_MINOR,
+       RNG_MODULE_NAME,
+       &rng_chrdev_ops,
+};
+
+/*
+ * rng_init - initialize RNG module
+ */
+static int __init rng_init (void)
+{
+       int err;
+
+        err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0);
+        if(err < 0)
+                goto out;
+
+        random_fd = err;
+
+        err = os_set_fd_block(random_fd, 0);
+        if(err)
+               goto err_out_cleanup_hw;
+
+       err = misc_register (&rng_miscdev);
+       if (err) {
+               printk (KERN_ERR RNG_MODULE_NAME ": misc device register failed\n");
+               goto err_out_cleanup_hw;
+       }
+
+ out:
+        return err;
+
+ err_out_cleanup_hw:
+        random_fd = -1;
+        goto out;
+}
+
+/*
+ * rng_cleanup - shutdown RNG module
+ */
+static void __exit rng_cleanup (void)
+{
+       misc_deregister (&rng_miscdev);
+}
+
+module_init (rng_init);
+module_exit (rng_cleanup);
+
+MODULE_DESCRIPTION("UML Host Random Number Generator (RNG) driver");
+MODULE_LICENSE("GPL");
diff --git a/arch/um/include/common-offsets.h b/arch/um/include/common-offsets.h
new file mode 100644 (file)
index 0000000..d705daa
--- /dev/null
@@ -0,0 +1,14 @@
+/* for use by sys-$SUBARCH/kernel-offsets.c */
+
+OFFSET(TASK_REGS, task_struct, thread.regs);
+OFFSET(TASK_PID, task_struct, pid);
+DEFINE(UM_KERN_PAGE_SIZE, PAGE_SIZE);
+DEFINE(UM_NSEC_PER_SEC, NSEC_PER_SEC);
+DEFINE_STR(UM_KERN_EMERG, KERN_EMERG);
+DEFINE_STR(UM_KERN_ALERT, KERN_ALERT);
+DEFINE_STR(UM_KERN_CRIT, KERN_CRIT);
+DEFINE_STR(UM_KERN_ERR, KERN_ERR);
+DEFINE_STR(UM_KERN_WARNING, KERN_WARNING);
+DEFINE_STR(UM_KERN_NOTICE, KERN_NOTICE);
+DEFINE_STR(UM_KERN_INFO, KERN_INFO);
+DEFINE_STR(UM_KERN_DEBUG, KERN_DEBUG);
diff --git a/arch/um/os-Linux/util/Makefile b/arch/um/os-Linux/util/Makefile
new file mode 100644 (file)
index 0000000..9778aed
--- /dev/null
@@ -0,0 +1,4 @@
+hostprogs-y            := mk_user_constants
+always                 := $(hostprogs-y)
+
+HOSTCFLAGS_mk_user_constants.o := -I$(objtree)/arch/um
diff --git a/arch/um/os-Linux/util/mk_user_constants.c b/arch/um/os-Linux/util/mk_user_constants.c
new file mode 100644 (file)
index 0000000..4838f30
--- /dev/null
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <user-offsets.h>
+
+int main(int argc, char **argv)
+{
+  printf("/*\n");
+  printf(" * Generated by mk_user_constants\n");
+  printf(" */\n");
+  printf("\n");
+  printf("#ifndef __UM_USER_CONSTANTS_H\n");
+  printf("#define __UM_USER_CONSTANTS_H\n");
+  printf("\n");
+  /* I'd like to use FRAME_SIZE from ptrace.h here, but that's wrong on
+   * x86_64 (216 vs 168 bytes).  user_regs_struct is the correct size on
+   * both x86_64 and i386.
+   */
+  printf("#define UM_FRAME_SIZE %d\n", __UM_FRAME_SIZE);
+
+  printf("\n");
+  printf("#endif\n");
+
+  return(0);
+}
diff --git a/arch/um/scripts/Makefile.rules b/arch/um/scripts/Makefile.rules
new file mode 100644 (file)
index 0000000..98346c7
--- /dev/null
@@ -0,0 +1,28 @@
+# ===========================================================================
+# arch/um: Generic definitions
+# ===========================================================================
+
+USER_SINGLE_OBJS := \
+       $(foreach f,$(patsubst %.o,%,$(obj-y) $(obj-m)),$($(f)-objs))
+USER_OBJS += $(filter %_user.o,$(obj-y) $(obj-m)  $(USER_SINGLE_OBJS))
+USER_OBJS := $(foreach file,$(USER_OBJS),$(obj)/$(file))
+
+$(USER_OBJS) : c_flags = -Wp,-MD,$(depfile) $(USER_CFLAGS) \
+       $(CFLAGS_$(notdir $@))
+
+quiet_cmd_make_link = SYMLINK $@
+cmd_make_link       = ln -sf $(srctree)/arch/$(SUBARCH)/$($(notdir $@)-dir)/$(notdir $@) $@
+
+# this needs to be before the foreach, because targets does not accept
+# complete paths like $(obj)/$(f). To make sure this works, use a := assignment
+# or we will get $(obj)/$(f) in the "targets" value.
+# Also, this forces you to use the := syntax when assigning to targets.
+# Otherwise the line below will cause an infinite loop (if you don't know why,
+# just do it).
+
+targets := $(targets) $(SYMLINKS)
+
+SYMLINKS := $(foreach f,$(SYMLINKS),$(obj)/$(f))
+
+$(SYMLINKS): FORCE
+       $(call if_changed,make_link)
diff --git a/arch/um/sys-i386/kernel-offsets.c b/arch/um/sys-i386/kernel-offsets.c
new file mode 100644 (file)
index 0000000..9f8ecd1
--- /dev/null
@@ -0,0 +1,25 @@
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <asm/page.h>
+
+#define DEFINE(sym, val) \
+        asm volatile("\n->" #sym " %0 " #val : : "i" (val))
+
+#define STR(x) #x
+#define DEFINE_STR(sym, val) asm volatile("\n->" #sym " " STR(val) " " #val: : )
+
+#define BLANK() asm volatile("\n->" : : )
+
+#define OFFSET(sym, str, mem) \
+       DEFINE(sym, offsetof(struct str, mem));
+
+void foo(void)
+{
+       OFFSET(TASK_DEBUGREGS, task_struct, thread.arch.debugregs);
+#ifdef CONFIG_MODE_TT
+       OFFSET(TASK_EXTERN_PID, task_struct, thread.mode.tt.extern_pid);
+#endif
+#include <common-offsets.h>
+}
diff --git a/arch/um/sys-i386/sys_call_table.S b/arch/um/sys-i386/sys_call_table.S
new file mode 100644 (file)
index 0000000..ad75c27
--- /dev/null
@@ -0,0 +1,16 @@
+#include <linux/linkage.h>
+/* Steal i386 syscall table for our purposes, but with some slight changes.*/
+
+#define sys_iopl sys_ni_syscall
+#define sys_ioperm sys_ni_syscall
+
+#define sys_vm86old sys_ni_syscall
+#define sys_vm86 sys_ni_syscall
+#define sys_set_thread_area sys_ni_syscall
+#define sys_get_thread_area sys_ni_syscall
+
+#define sys_stime um_stime
+#define sys_time um_time
+#define old_mmap old_mmap_i386
+
+#include "../../i386/kernel/syscall_table.S"
diff --git a/arch/um/sys-i386/user-offsets.c b/arch/um/sys-i386/user-offsets.c
new file mode 100644 (file)
index 0000000..3ceaabc
--- /dev/null
@@ -0,0 +1,69 @@
+#include <stdio.h>
+#include <signal.h>
+#include <asm/ptrace.h>
+#include <asm/user.h>
+#include <linux/stddef.h>
+
+#define DEFINE(sym, val) \
+        asm volatile("\n->" #sym " %0 " #val : : "i" (val))
+
+#define OFFSET(sym, str, mem) \
+       DEFINE(sym, offsetof(struct str, mem));
+
+void foo(void)
+{
+       OFFSET(SC_IP, sigcontext, eip);
+       OFFSET(SC_SP, sigcontext, esp);
+       OFFSET(SC_FS, sigcontext, fs);
+       OFFSET(SC_GS, sigcontext, gs);
+       OFFSET(SC_DS, sigcontext, ds);
+       OFFSET(SC_ES, sigcontext, es);
+       OFFSET(SC_SS, sigcontext, ss);
+       OFFSET(SC_CS, sigcontext, cs);
+       OFFSET(SC_EFLAGS, sigcontext, eflags);
+       OFFSET(SC_EAX, sigcontext, eax);
+       OFFSET(SC_EBX, sigcontext, ebx);
+       OFFSET(SC_ECX, sigcontext, ecx);
+       OFFSET(SC_EDX, sigcontext, edx);
+       OFFSET(SC_EDI, sigcontext, edi);
+       OFFSET(SC_ESI, sigcontext, esi);
+       OFFSET(SC_EBP, sigcontext, ebp);
+       OFFSET(SC_TRAPNO, sigcontext, trapno);
+       OFFSET(SC_ERR, sigcontext, err);
+       OFFSET(SC_CR2, sigcontext, cr2);
+       OFFSET(SC_FPSTATE, sigcontext, fpstate);
+       OFFSET(SC_SIGMASK, sigcontext, oldmask);
+       OFFSET(SC_FP_CW, _fpstate, cw);
+       OFFSET(SC_FP_SW, _fpstate, sw);
+       OFFSET(SC_FP_TAG, _fpstate, tag);
+       OFFSET(SC_FP_IPOFF, _fpstate, ipoff);
+       OFFSET(SC_FP_CSSEL, _fpstate, cssel);
+       OFFSET(SC_FP_DATAOFF, _fpstate, dataoff);
+       OFFSET(SC_FP_DATASEL, _fpstate, datasel);
+       OFFSET(SC_FP_ST, _fpstate, _st);
+       OFFSET(SC_FXSR_ENV, _fpstate, _fxsr_env);
+
+       DEFINE(HOST_FRAME_SIZE, FRAME_SIZE);
+       DEFINE(HOST_FP_SIZE,
+               sizeof(struct user_i387_struct) / sizeof(unsigned long));
+       DEFINE(HOST_XFP_SIZE,
+              sizeof(struct user_fxsr_struct) / sizeof(unsigned long));
+
+       DEFINE(HOST_IP, EIP);
+       DEFINE(HOST_SP, UESP);
+       DEFINE(HOST_EFLAGS, EFL);
+       DEFINE(HOST_EAX, EAX);
+       DEFINE(HOST_EBX, EBX);
+       DEFINE(HOST_ECX, ECX);
+       DEFINE(HOST_EDX, EDX);
+       DEFINE(HOST_ESI, ESI);
+       DEFINE(HOST_EDI, EDI);
+       DEFINE(HOST_EBP, EBP);
+       DEFINE(HOST_CS, CS);
+       DEFINE(HOST_SS, SS);
+       DEFINE(HOST_DS, DS);
+       DEFINE(HOST_FS, FS);
+       DEFINE(HOST_ES, ES);
+       DEFINE(HOST_GS, GS);
+       DEFINE(__UM_FRAME_SIZE, sizeof(struct user_regs_struct));
+}
diff --git a/arch/um/sys-i386/util/mk_thread.c b/arch/um/sys-i386/util/mk_thread.c
new file mode 100644 (file)
index 0000000..7470d0d
--- /dev/null
@@ -0,0 +1,22 @@
+#include <stdio.h>
+#include <kernel-offsets.h>
+
+int main(int argc, char **argv)
+{
+  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");
+  printf("#define TASK_DEBUGREGS(task) ((unsigned long *) "
+        "&(((char *) (task))[%d]))\n", TASK_DEBUGREGS);
+#ifdef TASK_EXTERN_PID
+  printf("#define TASK_EXTERN_PID(task) *((int *) &(((char *) (task))[%d]))\n",
+        TASK_EXTERN_PID);
+#endif
+  printf("\n");
+  printf("#endif\n");
+  return(0);
+}
diff --git a/arch/um/sys-x86_64/kernel-offsets.c b/arch/um/sys-x86_64/kernel-offsets.c
new file mode 100644 (file)
index 0000000..220e875
--- /dev/null
@@ -0,0 +1,24 @@
+#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <asm/page.h>
+
+#define DEFINE(sym, val) \
+        asm volatile("\n->" #sym " %0 " #val : : "i" (val))
+
+#define DEFINE_STR1(x) #x
+#define DEFINE_STR(sym, val) asm volatile("\n->" #sym " " DEFINE_STR1(val) " " #val: : )
+
+#define BLANK() asm volatile("\n->" : : )
+
+#define OFFSET(sym, str, mem) \
+       DEFINE(sym, offsetof(struct str, mem));
+
+void foo(void)
+{
+#ifdef CONFIG_MODE_TT
+       OFFSET(TASK_EXTERN_PID, task_struct, thread.mode.tt.extern_pid);
+#endif
+#include <common-offsets.h>
+}
diff --git a/arch/um/sys-x86_64/ksyms.c b/arch/um/sys-x86_64/ksyms.c
new file mode 100644 (file)
index 0000000..8592738
--- /dev/null
@@ -0,0 +1,19 @@
+#include "linux/module.h"
+#include "linux/in6.h"
+#include "linux/rwsem.h"
+#include "asm/byteorder.h"
+#include "asm/semaphore.h"
+#include "asm/uaccess.h"
+#include "asm/checksum.h"
+#include "asm/errno.h"
+
+EXPORT_SYMBOL(__down_failed);
+EXPORT_SYMBOL(__down_failed_interruptible);
+EXPORT_SYMBOL(__down_failed_trylock);
+EXPORT_SYMBOL(__up_wakeup);
+
+/*XXX: we need them because they would be exported by x86_64 */
+EXPORT_SYMBOL(__memcpy);
+
+/* Networking helper routines. */
+EXPORT_SYMBOL(ip_compute_csum);
diff --git a/arch/um/sys-x86_64/syscall_table.c b/arch/um/sys-x86_64/syscall_table.c
new file mode 100644 (file)
index 0000000..34b2e84
--- /dev/null
@@ -0,0 +1,59 @@
+/* System call table for UML/x86-64, copied from arch/x86_64/kernel/syscall.c
+ * with some changes for UML. */
+
+#include <linux/linkage.h>
+#include <linux/sys.h>
+#include <linux/cache.h>
+#include <linux/config.h>
+
+#define __NO_STUBS
+
+/* Below you can see, in terms of #define's, the differences between the x86-64
+ * and the UML syscall table. */
+
+/* Not going to be implemented by UML, since we have no hardware. */
+#define stub_iopl sys_ni_syscall
+#define sys_ioperm sys_ni_syscall
+
+/* The UML TLS problem. Note that x86_64 does not implement this, so the below
+ * is needed only for the ia32 compatibility. */
+/*#define sys_set_thread_area sys_ni_syscall
+#define sys_get_thread_area sys_ni_syscall*/
+
+/* For __NR_time. The x86-64 name hopefully will change from sys_time64 to
+ * sys_time (since the current situation is bogus). I've sent a patch to cleanup
+ * this. Remove below the obsoleted line. */
+#define sys_time64 um_time
+#define sys_time um_time
+
+/* On UML we call it this way ("old" means it's not mmap2) */
+#define sys_mmap old_mmap
+/* On x86-64 sys_uname is actually sys_newuname plus a compatibility trick.
+ * See arch/x86_64/kernel/sys_x86_64.c */
+#define sys_uname sys_uname64
+
+#define stub_clone sys_clone
+#define stub_fork sys_fork
+#define stub_vfork sys_vfork
+#define stub_execve sys_execve
+#define stub_rt_sigsuspend sys_rt_sigsuspend
+#define stub_sigaltstack sys_sigaltstack
+#define stub_rt_sigreturn sys_rt_sigreturn
+
+#define __SYSCALL(nr, sym) extern asmlinkage void sym(void) ;
+#undef _ASM_X86_64_UNISTD_H_
+#include <asm-x86_64/unistd.h>
+
+#undef __SYSCALL
+#define __SYSCALL(nr, sym) [ nr ] = sym,
+#undef _ASM_X86_64_UNISTD_H_
+
+typedef void (*sys_call_ptr_t)(void);
+
+extern void sys_ni_syscall(void);
+
+sys_call_ptr_t sys_call_table[__NR_syscall_max+1] __cacheline_aligned = {
+       /* Smells like a like a compiler bug -- it doesn't work when the & below is removed. */
+       [0 ... __NR_syscall_max] = &sys_ni_syscall,
+#include <asm-x86_64/unistd.h>
+};
diff --git a/arch/um/sys-x86_64/user-offsets.c b/arch/um/sys-x86_64/user-offsets.c
new file mode 100644 (file)
index 0000000..513d17c
--- /dev/null
@@ -0,0 +1,86 @@
+#include <stdio.h>
+#include <stddef.h>
+#include <signal.h>
+#define __FRAME_OFFSETS
+#include <asm/ptrace.h>
+#include <asm/types.h>
+/* For some reason, x86_64 defines u64 and u32 only in <pci/types.h>, which I
+ * refuse to include here, even though they're used throughout the headers.
+ * These are used in asm/user.h, and that include can't be avoided because of
+ * the sizeof(struct user_regs_struct) below.
+ */
+typedef __u64 u64;
+typedef __u32 u32;
+#include <asm/user.h>
+
+#define DEFINE(sym, val) \
+        asm volatile("\n->" #sym " %0 " #val : : "i" (val))
+
+#define OFFSET(sym, str, mem) \
+       DEFINE(sym, offsetof(struct str, mem));
+
+void foo(void)
+{
+       OFFSET(SC_RBX, sigcontext, rbx);
+       OFFSET(SC_RCX, sigcontext, rcx);
+       OFFSET(SC_RDX, sigcontext, rdx);
+       OFFSET(SC_RSI, sigcontext, rsi);
+       OFFSET(SC_RDI, sigcontext, rdi);
+       OFFSET(SC_RBP, sigcontext, rbp);
+       OFFSET(SC_RAX, sigcontext, rax);
+       OFFSET(SC_R8, sigcontext, r8);
+       OFFSET(SC_R9, sigcontext, r9);
+       OFFSET(SC_R10, sigcontext, r10);
+       OFFSET(SC_R11, sigcontext, r11);
+       OFFSET(SC_R12, sigcontext, r12);
+       OFFSET(SC_R13, sigcontext, r13);
+       OFFSET(SC_R14, sigcontext, r14);
+       OFFSET(SC_R15, sigcontext, r15);
+       OFFSET(SC_IP, sigcontext, rip);
+       OFFSET(SC_SP, sigcontext, rsp);
+       OFFSET(SC_CR2, sigcontext, cr2);
+       OFFSET(SC_ERR, sigcontext, err);
+       OFFSET(SC_TRAPNO, sigcontext, trapno);
+       OFFSET(SC_CS, sigcontext, cs);
+       OFFSET(SC_FS, sigcontext, fs);
+       OFFSET(SC_GS, sigcontext, gs);
+       OFFSET(SC_EFLAGS, sigcontext, eflags);
+       OFFSET(SC_SIGMASK, sigcontext, oldmask);
+#if 0
+       OFFSET(SC_ORIG_RAX, sigcontext, orig_rax);
+       OFFSET(SC_DS, sigcontext, ds);
+       OFFSET(SC_ES, sigcontext, es);
+       OFFSET(SC_SS, sigcontext, ss);
+#endif
+
+       DEFINE(HOST_FRAME_SIZE, FRAME_SIZE);
+       DEFINE(HOST_RBX, RBX);
+       DEFINE(HOST_RCX, RCX);
+       DEFINE(HOST_RDI, RDI);
+       DEFINE(HOST_RSI, RSI);
+       DEFINE(HOST_RDX, RDX);
+       DEFINE(HOST_RBP, RBP);
+       DEFINE(HOST_RAX, RAX);
+       DEFINE(HOST_R8, R8);
+       DEFINE(HOST_R9, R9);
+       DEFINE(HOST_R10, R10);
+       DEFINE(HOST_R11, R11);
+       DEFINE(HOST_R12, R12);
+       DEFINE(HOST_R13, R13);
+       DEFINE(HOST_R14, R14);
+       DEFINE(HOST_R15, R15);
+       DEFINE(HOST_ORIG_RAX, ORIG_RAX);
+       DEFINE(HOST_CS, CS);
+       DEFINE(HOST_SS, SS);
+       DEFINE(HOST_EFLAGS, EFLAGS);
+#if 0
+       DEFINE(HOST_FS, FS);
+       DEFINE(HOST_GS, GS);
+       DEFINE(HOST_DS, DS);
+       DEFINE(HOST_ES, ES);
+#endif
+
+       DEFINE(HOST_IP, RIP);
+       DEFINE(HOST_SP, RSP);
+       DEFINE(__UM_FRAME_SIZE, sizeof(struct user_regs_struct));
+}
diff --git a/arch/um/sys-x86_64/util/mk_thread.c b/arch/um/sys-x86_64/util/mk_thread.c
new file mode 100644 (file)
index 0000000..1551739
--- /dev/null
@@ -0,0 +1,20 @@
+#include <stdio.h>
+#include <kernel-offsets.h>
+
+int main(int argc, char **argv)
+{
+  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");
+#ifdef TASK_EXTERN_PID
+  printf("#define TASK_EXTERN_PID(task) *((int *) &(((char *) (task))[%d]))\n",
+        TASK_EXTERN_PID);
+#endif
+  printf("\n");
+  printf("#endif\n");
+  return(0);
+}
diff --git a/arch/um/util/mk_constants.c b/arch/um/util/mk_constants.c
new file mode 100644 (file)
index 0000000..ab217be
--- /dev/null
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <kernel-offsets.h>
+
+#define SHOW_INT(sym) printf("#define %s %d\n", #sym, sym)
+#define SHOW_STR(sym) printf("#define %s %s\n", #sym, sym)
+
+int main(int argc, char **argv)
+{
+  printf("/*\n");
+  printf(" * Generated by mk_constants\n");
+  printf(" */\n");
+  printf("\n");
+  printf("#ifndef __UM_CONSTANTS_H\n");
+  printf("#define __UM_CONSTANTS_H\n");
+  printf("\n");
+
+  SHOW_INT(UM_KERN_PAGE_SIZE);
+
+  SHOW_STR(UM_KERN_EMERG);
+  SHOW_STR(UM_KERN_ALERT);
+  SHOW_STR(UM_KERN_CRIT);
+  SHOW_STR(UM_KERN_ERR);
+  SHOW_STR(UM_KERN_WARNING);
+  SHOW_STR(UM_KERN_NOTICE);
+  SHOW_STR(UM_KERN_INFO);
+  SHOW_STR(UM_KERN_DEBUG);
+
+  SHOW_INT(UM_NSEC_PER_SEC);
+  printf("\n");
+  printf("#endif\n");
+  return(0);
+}
diff --git a/arch/um/util/mk_task.c b/arch/um/util/mk_task.c
new file mode 100644 (file)
index 0000000..36c9606
--- /dev/null
@@ -0,0 +1,30 @@
+#include <stdio.h>
+#include <kernel-offsets.h>
+
+void print_ptr(char *name, char *type, int offset)
+{
+  printf("#define %s(task) ((%s *) &(((char *) (task))[%d]))\n", name, type,
+        offset);
+}
+
+void print(char *name, char *type, int offset)
+{
+  printf("#define %s(task) *((%s *) &(((char *) (task))[%d]))\n", name, type,
+        offset);
+}
+
+int main(int argc, char **argv)
+{
+  printf("/*\n");
+  printf(" * Generated by mk_task\n");
+  printf(" */\n");
+  printf("\n");
+  printf("#ifndef __TASK_H\n");
+  printf("#define __TASK_H\n");
+  printf("\n");
+  print_ptr("TASK_REGS", "union uml_pt_regs", TASK_REGS);
+  print("TASK_PID", "int", TASK_PID);
+  printf("\n");
+  printf("#endif\n");
+  return(0);
+}
diff --git a/arch/x86_64/kernel/pmtimer.c b/arch/x86_64/kernel/pmtimer.c
new file mode 100644 (file)
index 0000000..feb5f10
--- /dev/null
@@ -0,0 +1,101 @@
+/* Ported over from i386 by AK, original copyright was:
+ *
+ * (C) Dominik Brodowski <linux@brodo.de> 2003
+ *
+ * Driver to use the Power Management Timer (PMTMR) available in some
+ * southbridges as primary timing source for the Linux kernel.
+ *
+ * Based on parts of linux/drivers/acpi/hardware/hwtimer.c, timer_pit.c,
+ * timer_hpet.c, and on Arjan van de Ven's implementation for 2.4.
+ *
+ * This file is licensed under the GPL v2.
+ *
+ * Dropped all the hardware bug workarounds for now. Hopefully they
+ * are not needed on 64bit chipsets.
+ */
+
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/cpumask.h>
+#include <asm/io.h>
+#include <asm/proto.h>
+#include <asm/msr.h>
+#include <asm/vsyscall.h>
+
+/* The I/O port the PMTMR resides at.
+ * The location is detected during setup_arch(),
+ * in arch/i386/kernel/acpi/boot.c */
+u32 pmtmr_ioport;
+
+/* value of the Power timer at last timer interrupt */
+static u32 offset_delay;
+static u32 last_pmtmr_tick;
+
+#define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */
+
+static inline u32 cyc2us(u32 cycles)
+{
+       /* The Power Management Timer ticks at 3.579545 ticks per microsecond.
+        * 1 / PM_TIMER_FREQUENCY == 0.27936511 =~ 286/1024 [error: 0.024%]
+        *
+        * Even with HZ = 100, delta is at maximum 35796 ticks, so it can
+        * easily be multiplied with 286 (=0x11E) without having to fear
+        * u32 overflows.
+        */
+       cycles *= 286;
+       return (cycles >> 10);
+}
+
+int pmtimer_mark_offset(void)
+{
+       static int first_run = 1;
+       unsigned long tsc;
+       u32 lost;
+
+       u32 tick = inl(pmtmr_ioport);
+       u32 delta;
+
+       delta = cyc2us((tick - last_pmtmr_tick) & ACPI_PM_MASK);
+
+       last_pmtmr_tick = tick;
+       monotonic_base += delta * NSEC_PER_USEC;
+
+       delta += offset_delay;
+
+       lost = delta / (USEC_PER_SEC / HZ);
+       offset_delay = delta % (USEC_PER_SEC / HZ);
+
+       rdtscll(tsc);
+       vxtime.last_tsc = tsc - offset_delay * cpu_khz;
+
+       /* don't calculate delay for first run,
+          or if we've got less then a tick */
+       if (first_run || (lost < 1)) {
+               first_run = 0;
+               offset_delay = 0;
+       }
+
+       return lost - 1;
+}
+
+unsigned int do_gettimeoffset_pm(void)
+{
+       u32 now, offset, delta = 0;
+
+       offset = last_pmtmr_tick;
+       now = inl(pmtmr_ioport);
+       delta = (now - offset) & ACPI_PM_MASK;
+
+       return offset_delay + cyc2us(delta);
+}
+
+
+static int __init nopmtimer_setup(char *s)
+{
+       pmtmr_ioport = 0;
+       return 0;
+}
+
+__setup("nopmtimer", nopmtimer_setup);
diff --git a/crypto/tgr192.c b/crypto/tgr192.c
new file mode 100644 (file)
index 0000000..f0a45cf
--- /dev/null
@@ -0,0 +1,735 @@
+/*
+ * Cryptographic API.
+ *
+ * Tiger hashing Algorithm
+ *
+ *      Copyright (C) 1998 Free Software Foundation, Inc.
+ *
+ * The Tiger algorithm was developed by Ross Anderson and Eli Biham.
+ * It was optimized for 64-bit processors while still delievering
+ * decent performance on 32 and 16-bit processors.
+ *
+ * This version is derived from the GnuPG implementation and the
+ * Tiger-Perl interface written by Rafael Sevilla
+ *
+ * Adapted for Linux Kernel Crypto  by Aaron Grothe 
+ * ajgrothe@yahoo.com, February 22, 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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 TGR192_DIGEST_SIZE 24
+#define TGR160_DIGEST_SIZE 20
+#define TGR128_DIGEST_SIZE 16
+
+#define TGR192_BLOCK_SIZE  64
+
+struct tgr192_ctx {
+       u64 a, b, c;
+       u8 hash[64];
+       int count;
+       u32 nblocks;
+};
+
+static const u64 sbox1[256] = {
+       0x02aab17cf7e90c5eULL, 0xac424b03e243a8ecULL, 0x72cd5be30dd5fcd3ULL,
+       0x6d019b93f6f97f3aULL, 0xcd9978ffd21f9193ULL, 0x7573a1c9708029e2ULL,
+       0xb164326b922a83c3ULL, 0x46883eee04915870ULL, 0xeaace3057103ece6ULL,
+       0xc54169b808a3535cULL, 0x4ce754918ddec47cULL, 0x0aa2f4dfdc0df40cULL,
+       0x10b76f18a74dbefaULL, 0xc6ccb6235ad1ab6aULL, 0x13726121572fe2ffULL,
+       0x1a488c6f199d921eULL, 0x4bc9f9f4da0007caULL, 0x26f5e6f6e85241c7ULL,
+       0x859079dbea5947b6ULL, 0x4f1885c5c99e8c92ULL, 0xd78e761ea96f864bULL,
+       0x8e36428c52b5c17dULL, 0x69cf6827373063c1ULL, 0xb607c93d9bb4c56eULL,
+       0x7d820e760e76b5eaULL, 0x645c9cc6f07fdc42ULL, 0xbf38a078243342e0ULL,
+       0x5f6b343c9d2e7d04ULL, 0xf2c28aeb600b0ec6ULL, 0x6c0ed85f7254bcacULL,
+       0x71592281a4db4fe5ULL, 0x1967fa69ce0fed9fULL, 0xfd5293f8b96545dbULL,
+       0xc879e9d7f2a7600bULL, 0x860248920193194eULL, 0xa4f9533b2d9cc0b3ULL,
+       0x9053836c15957613ULL, 0xdb6dcf8afc357bf1ULL, 0x18beea7a7a370f57ULL,
+       0x037117ca50b99066ULL, 0x6ab30a9774424a35ULL, 0xf4e92f02e325249bULL,
+       0x7739db07061ccae1ULL, 0xd8f3b49ceca42a05ULL, 0xbd56be3f51382f73ULL,
+       0x45faed5843b0bb28ULL, 0x1c813d5c11bf1f83ULL, 0x8af0e4b6d75fa169ULL,
+       0x33ee18a487ad9999ULL, 0x3c26e8eab1c94410ULL, 0xb510102bc0a822f9ULL,
+       0x141eef310ce6123bULL, 0xfc65b90059ddb154ULL, 0xe0158640c5e0e607ULL,
+       0x884e079826c3a3cfULL, 0x930d0d9523c535fdULL, 0x35638d754e9a2b00ULL,
+       0x4085fccf40469dd5ULL, 0xc4b17ad28be23a4cULL, 0xcab2f0fc6a3e6a2eULL,
+       0x2860971a6b943fcdULL, 0x3dde6ee212e30446ULL, 0x6222f32ae01765aeULL,
+       0x5d550bb5478308feULL, 0xa9efa98da0eda22aULL, 0xc351a71686c40da7ULL,
+       0x1105586d9c867c84ULL, 0xdcffee85fda22853ULL, 0xccfbd0262c5eef76ULL,
+       0xbaf294cb8990d201ULL, 0xe69464f52afad975ULL, 0x94b013afdf133e14ULL,
+       0x06a7d1a32823c958ULL, 0x6f95fe5130f61119ULL, 0xd92ab34e462c06c0ULL,
+       0xed7bde33887c71d2ULL, 0x79746d6e6518393eULL, 0x5ba419385d713329ULL,
+       0x7c1ba6b948a97564ULL, 0x31987c197bfdac67ULL, 0xde6c23c44b053d02ULL,
+       0x581c49fed002d64dULL, 0xdd474d6338261571ULL, 0xaa4546c3e473d062ULL,
+       0x928fce349455f860ULL, 0x48161bbacaab94d9ULL, 0x63912430770e6f68ULL,
+       0x6ec8a5e602c6641cULL, 0x87282515337ddd2bULL, 0x2cda6b42034b701bULL,
+       0xb03d37c181cb096dULL, 0xe108438266c71c6fULL, 0x2b3180c7eb51b255ULL,
+       0xdf92b82f96c08bbcULL, 0x5c68c8c0a632f3baULL, 0x5504cc861c3d0556ULL,
+       0xabbfa4e55fb26b8fULL, 0x41848b0ab3baceb4ULL, 0xb334a273aa445d32ULL,
+       0xbca696f0a85ad881ULL, 0x24f6ec65b528d56cULL, 0x0ce1512e90f4524aULL,
+       0x4e9dd79d5506d35aULL, 0x258905fac6ce9779ULL, 0x2019295b3e109b33ULL,
+       0xf8a9478b73a054ccULL, 0x2924f2f934417eb0ULL, 0x3993357d536d1bc4ULL,
+       0x38a81ac21db6ff8bULL, 0x47c4fbf17d6016bfULL, 0x1e0faadd7667e3f5ULL,
+       0x7abcff62938beb96ULL, 0xa78dad948fc179c9ULL, 0x8f1f98b72911e50dULL,
+       0x61e48eae27121a91ULL, 0x4d62f7ad31859808ULL, 0xeceba345ef5ceaebULL,
+       0xf5ceb25ebc9684ceULL, 0xf633e20cb7f76221ULL, 0xa32cdf06ab8293e4ULL,
+       0x985a202ca5ee2ca4ULL, 0xcf0b8447cc8a8fb1ULL, 0x9f765244979859a3ULL,
+       0xa8d516b1a1240017ULL, 0x0bd7ba3ebb5dc726ULL, 0xe54bca55b86adb39ULL,
+       0x1d7a3afd6c478063ULL, 0x519ec608e7669eddULL, 0x0e5715a2d149aa23ULL,
+       0x177d4571848ff194ULL, 0xeeb55f3241014c22ULL, 0x0f5e5ca13a6e2ec2ULL,
+       0x8029927b75f5c361ULL, 0xad139fabc3d6e436ULL, 0x0d5df1a94ccf402fULL,
+       0x3e8bd948bea5dfc8ULL, 0xa5a0d357bd3ff77eULL, 0xa2d12e251f74f645ULL,
+       0x66fd9e525e81a082ULL, 0x2e0c90ce7f687a49ULL, 0xc2e8bcbeba973bc5ULL,
+       0x000001bce509745fULL, 0x423777bbe6dab3d6ULL, 0xd1661c7eaef06eb5ULL,
+       0xa1781f354daacfd8ULL, 0x2d11284a2b16affcULL, 0xf1fc4f67fa891d1fULL,
+       0x73ecc25dcb920adaULL, 0xae610c22c2a12651ULL, 0x96e0a810d356b78aULL,
+       0x5a9a381f2fe7870fULL, 0xd5ad62ede94e5530ULL, 0xd225e5e8368d1427ULL,
+       0x65977b70c7af4631ULL, 0x99f889b2de39d74fULL, 0x233f30bf54e1d143ULL,
+       0x9a9675d3d9a63c97ULL, 0x5470554ff334f9a8ULL, 0x166acb744a4f5688ULL,
+       0x70c74caab2e4aeadULL, 0xf0d091646f294d12ULL, 0x57b82a89684031d1ULL,
+       0xefd95a5a61be0b6bULL, 0x2fbd12e969f2f29aULL, 0x9bd37013feff9fe8ULL,
+       0x3f9b0404d6085a06ULL, 0x4940c1f3166cfe15ULL, 0x09542c4dcdf3defbULL,
+       0xb4c5218385cd5ce3ULL, 0xc935b7dc4462a641ULL, 0x3417f8a68ed3b63fULL,
+       0xb80959295b215b40ULL, 0xf99cdaef3b8c8572ULL, 0x018c0614f8fcb95dULL,
+       0x1b14accd1a3acdf3ULL, 0x84d471f200bb732dULL, 0xc1a3110e95e8da16ULL,
+       0x430a7220bf1a82b8ULL, 0xb77e090d39df210eULL, 0x5ef4bd9f3cd05e9dULL,
+       0x9d4ff6da7e57a444ULL, 0xda1d60e183d4a5f8ULL, 0xb287c38417998e47ULL,
+       0xfe3edc121bb31886ULL, 0xc7fe3ccc980ccbefULL, 0xe46fb590189bfd03ULL,
+       0x3732fd469a4c57dcULL, 0x7ef700a07cf1ad65ULL, 0x59c64468a31d8859ULL,
+       0x762fb0b4d45b61f6ULL, 0x155baed099047718ULL, 0x68755e4c3d50baa6ULL,
+       0xe9214e7f22d8b4dfULL, 0x2addbf532eac95f4ULL, 0x32ae3909b4bd0109ULL,
+       0x834df537b08e3450ULL, 0xfa209da84220728dULL, 0x9e691d9b9efe23f7ULL,
+       0x0446d288c4ae8d7fULL, 0x7b4cc524e169785bULL, 0x21d87f0135ca1385ULL,
+       0xcebb400f137b8aa5ULL, 0x272e2b66580796beULL, 0x3612264125c2b0deULL,
+       0x057702bdad1efbb2ULL, 0xd4babb8eacf84be9ULL, 0x91583139641bc67bULL,
+       0x8bdc2de08036e024ULL, 0x603c8156f49f68edULL, 0xf7d236f7dbef5111ULL,
+       0x9727c4598ad21e80ULL, 0xa08a0896670a5fd7ULL, 0xcb4a8f4309eba9cbULL,
+       0x81af564b0f7036a1ULL, 0xc0b99aa778199abdULL, 0x959f1ec83fc8e952ULL,
+       0x8c505077794a81b9ULL, 0x3acaaf8f056338f0ULL, 0x07b43f50627a6778ULL,
+       0x4a44ab49f5eccc77ULL, 0x3bc3d6e4b679ee98ULL, 0x9cc0d4d1cf14108cULL,
+       0x4406c00b206bc8a0ULL, 0x82a18854c8d72d89ULL, 0x67e366b35c3c432cULL,
+       0xb923dd61102b37f2ULL, 0x56ab2779d884271dULL, 0xbe83e1b0ff1525afULL,
+       0xfb7c65d4217e49a9ULL, 0x6bdbe0e76d48e7d4ULL, 0x08df828745d9179eULL,
+       0x22ea6a9add53bd34ULL, 0xe36e141c5622200aULL, 0x7f805d1b8cb750eeULL,
+       0xafe5c7a59f58e837ULL, 0xe27f996a4fb1c23cULL, 0xd3867dfb0775f0d0ULL,
+       0xd0e673de6e88891aULL, 0x123aeb9eafb86c25ULL, 0x30f1d5d5c145b895ULL,
+       0xbb434a2dee7269e7ULL, 0x78cb67ecf931fa38ULL, 0xf33b0372323bbf9cULL,
+       0x52d66336fb279c74ULL, 0x505f33ac0afb4eaaULL, 0xe8a5cd99a2cce187ULL,
+       0x534974801e2d30bbULL, 0x8d2d5711d5876d90ULL, 0x1f1a412891bc038eULL,
+       0xd6e2e71d82e56648ULL, 0x74036c3a497732b7ULL, 0x89b67ed96361f5abULL,
+       0xffed95d8f1ea02a2ULL, 0xe72b3bd61464d43dULL, 0xa6300f170bdc4820ULL,
+       0xebc18760ed78a77aULL
+};
+
+static const u64 sbox2[256] = {
+       0xe6a6be5a05a12138ULL, 0xb5a122a5b4f87c98ULL, 0x563c6089140b6990ULL,
+       0x4c46cb2e391f5dd5ULL, 0xd932addbc9b79434ULL, 0x08ea70e42015aff5ULL,
+       0xd765a6673e478cf1ULL, 0xc4fb757eab278d99ULL, 0xdf11c6862d6e0692ULL,
+       0xddeb84f10d7f3b16ULL, 0x6f2ef604a665ea04ULL, 0x4a8e0f0ff0e0dfb3ULL,
+       0xa5edeef83dbcba51ULL, 0xfc4f0a2a0ea4371eULL, 0xe83e1da85cb38429ULL,
+       0xdc8ff882ba1b1ce2ULL, 0xcd45505e8353e80dULL, 0x18d19a00d4db0717ULL,
+       0x34a0cfeda5f38101ULL, 0x0be77e518887caf2ULL, 0x1e341438b3c45136ULL,
+       0xe05797f49089ccf9ULL, 0xffd23f9df2591d14ULL, 0x543dda228595c5cdULL,
+       0x661f81fd99052a33ULL, 0x8736e641db0f7b76ULL, 0x15227725418e5307ULL,
+       0xe25f7f46162eb2faULL, 0x48a8b2126c13d9feULL, 0xafdc541792e76eeaULL,
+       0x03d912bfc6d1898fULL, 0x31b1aafa1b83f51bULL, 0xf1ac2796e42ab7d9ULL,
+       0x40a3a7d7fcd2ebacULL, 0x1056136d0afbbcc5ULL, 0x7889e1dd9a6d0c85ULL,
+       0xd33525782a7974aaULL, 0xa7e25d09078ac09bULL, 0xbd4138b3eac6edd0ULL,
+       0x920abfbe71eb9e70ULL, 0xa2a5d0f54fc2625cULL, 0xc054e36b0b1290a3ULL,
+       0xf6dd59ff62fe932bULL, 0x3537354511a8ac7dULL, 0xca845e9172fadcd4ULL,
+       0x84f82b60329d20dcULL, 0x79c62ce1cd672f18ULL, 0x8b09a2add124642cULL,
+       0xd0c1e96a19d9e726ULL, 0x5a786a9b4ba9500cULL, 0x0e020336634c43f3ULL,
+       0xc17b474aeb66d822ULL, 0x6a731ae3ec9baac2ULL, 0x8226667ae0840258ULL,
+       0x67d4567691caeca5ULL, 0x1d94155c4875adb5ULL, 0x6d00fd985b813fdfULL,
+       0x51286efcb774cd06ULL, 0x5e8834471fa744afULL, 0xf72ca0aee761ae2eULL,
+       0xbe40e4cdaee8e09aULL, 0xe9970bbb5118f665ULL, 0x726e4beb33df1964ULL,
+       0x703b000729199762ULL, 0x4631d816f5ef30a7ULL, 0xb880b5b51504a6beULL,
+       0x641793c37ed84b6cULL, 0x7b21ed77f6e97d96ULL, 0x776306312ef96b73ULL,
+       0xae528948e86ff3f4ULL, 0x53dbd7f286a3f8f8ULL, 0x16cadce74cfc1063ULL,
+       0x005c19bdfa52c6ddULL, 0x68868f5d64d46ad3ULL, 0x3a9d512ccf1e186aULL,
+       0x367e62c2385660aeULL, 0xe359e7ea77dcb1d7ULL, 0x526c0773749abe6eULL,
+       0x735ae5f9d09f734bULL, 0x493fc7cc8a558ba8ULL, 0xb0b9c1533041ab45ULL,
+       0x321958ba470a59bdULL, 0x852db00b5f46c393ULL, 0x91209b2bd336b0e5ULL,
+       0x6e604f7d659ef19fULL, 0xb99a8ae2782ccb24ULL, 0xccf52ab6c814c4c7ULL,
+       0x4727d9afbe11727bULL, 0x7e950d0c0121b34dULL, 0x756f435670ad471fULL,
+       0xf5add442615a6849ULL, 0x4e87e09980b9957aULL, 0x2acfa1df50aee355ULL,
+       0xd898263afd2fd556ULL, 0xc8f4924dd80c8fd6ULL, 0xcf99ca3d754a173aULL,
+       0xfe477bacaf91bf3cULL, 0xed5371f6d690c12dULL, 0x831a5c285e687094ULL,
+       0xc5d3c90a3708a0a4ULL, 0x0f7f903717d06580ULL, 0x19f9bb13b8fdf27fULL,
+       0xb1bd6f1b4d502843ULL, 0x1c761ba38fff4012ULL, 0x0d1530c4e2e21f3bULL,
+       0x8943ce69a7372c8aULL, 0xe5184e11feb5ce66ULL, 0x618bdb80bd736621ULL,
+       0x7d29bad68b574d0bULL, 0x81bb613e25e6fe5bULL, 0x071c9c10bc07913fULL,
+       0xc7beeb7909ac2d97ULL, 0xc3e58d353bc5d757ULL, 0xeb017892f38f61e8ULL,
+       0xd4effb9c9b1cc21aULL, 0x99727d26f494f7abULL, 0xa3e063a2956b3e03ULL,
+       0x9d4a8b9a4aa09c30ULL, 0x3f6ab7d500090fb4ULL, 0x9cc0f2a057268ac0ULL,
+       0x3dee9d2dedbf42d1ULL, 0x330f49c87960a972ULL, 0xc6b2720287421b41ULL,
+       0x0ac59ec07c00369cULL, 0xef4eac49cb353425ULL, 0xf450244eef0129d8ULL,
+       0x8acc46e5caf4deb6ULL, 0x2ffeab63989263f7ULL, 0x8f7cb9fe5d7a4578ULL,
+       0x5bd8f7644e634635ULL, 0x427a7315bf2dc900ULL, 0x17d0c4aa2125261cULL,
+       0x3992486c93518e50ULL, 0xb4cbfee0a2d7d4c3ULL, 0x7c75d6202c5ddd8dULL,
+       0xdbc295d8e35b6c61ULL, 0x60b369d302032b19ULL, 0xce42685fdce44132ULL,
+       0x06f3ddb9ddf65610ULL, 0x8ea4d21db5e148f0ULL, 0x20b0fce62fcd496fULL,
+       0x2c1b912358b0ee31ULL, 0xb28317b818f5a308ULL, 0xa89c1e189ca6d2cfULL,
+       0x0c6b18576aaadbc8ULL, 0xb65deaa91299fae3ULL, 0xfb2b794b7f1027e7ULL,
+       0x04e4317f443b5bebULL, 0x4b852d325939d0a6ULL, 0xd5ae6beefb207ffcULL,
+       0x309682b281c7d374ULL, 0xbae309a194c3b475ULL, 0x8cc3f97b13b49f05ULL,
+       0x98a9422ff8293967ULL, 0x244b16b01076ff7cULL, 0xf8bf571c663d67eeULL,
+       0x1f0d6758eee30da1ULL, 0xc9b611d97adeb9b7ULL, 0xb7afd5887b6c57a2ULL,
+       0x6290ae846b984fe1ULL, 0x94df4cdeacc1a5fdULL, 0x058a5bd1c5483affULL,
+       0x63166cc142ba3c37ULL, 0x8db8526eb2f76f40ULL, 0xe10880036f0d6d4eULL,
+       0x9e0523c9971d311dULL, 0x45ec2824cc7cd691ULL, 0x575b8359e62382c9ULL,
+       0xfa9e400dc4889995ULL, 0xd1823ecb45721568ULL, 0xdafd983b8206082fULL,
+       0xaa7d29082386a8cbULL, 0x269fcd4403b87588ULL, 0x1b91f5f728bdd1e0ULL,
+       0xe4669f39040201f6ULL, 0x7a1d7c218cf04adeULL, 0x65623c29d79ce5ceULL,
+       0x2368449096c00bb1ULL, 0xab9bf1879da503baULL, 0xbc23ecb1a458058eULL,
+       0x9a58df01bb401eccULL, 0xa070e868a85f143dULL, 0x4ff188307df2239eULL,
+       0x14d565b41a641183ULL, 0xee13337452701602ULL, 0x950e3dcf3f285e09ULL,
+       0x59930254b9c80953ULL, 0x3bf299408930da6dULL, 0xa955943f53691387ULL,
+       0xa15edecaa9cb8784ULL, 0x29142127352be9a0ULL, 0x76f0371fff4e7afbULL,
+       0x0239f450274f2228ULL, 0xbb073af01d5e868bULL, 0xbfc80571c10e96c1ULL,
+       0xd267088568222e23ULL, 0x9671a3d48e80b5b0ULL, 0x55b5d38ae193bb81ULL,
+       0x693ae2d0a18b04b8ULL, 0x5c48b4ecadd5335fULL, 0xfd743b194916a1caULL,
+       0x2577018134be98c4ULL, 0xe77987e83c54a4adULL, 0x28e11014da33e1b9ULL,
+       0x270cc59e226aa213ULL, 0x71495f756d1a5f60ULL, 0x9be853fb60afef77ULL,
+       0xadc786a7f7443dbfULL, 0x0904456173b29a82ULL, 0x58bc7a66c232bd5eULL,
+       0xf306558c673ac8b2ULL, 0x41f639c6b6c9772aULL, 0x216defe99fda35daULL,
+       0x11640cc71c7be615ULL, 0x93c43694565c5527ULL, 0xea038e6246777839ULL,
+       0xf9abf3ce5a3e2469ULL, 0x741e768d0fd312d2ULL, 0x0144b883ced652c6ULL,
+       0xc20b5a5ba33f8552ULL, 0x1ae69633c3435a9dULL, 0x97a28ca4088cfdecULL,
+       0x8824a43c1e96f420ULL, 0x37612fa66eeea746ULL, 0x6b4cb165f9cf0e5aULL,
+       0x43aa1c06a0abfb4aULL, 0x7f4dc26ff162796bULL, 0x6cbacc8e54ed9b0fULL,
+       0xa6b7ffefd2bb253eULL, 0x2e25bc95b0a29d4fULL, 0x86d6a58bdef1388cULL,
+       0xded74ac576b6f054ULL, 0x8030bdbc2b45805dULL, 0x3c81af70e94d9289ULL,
+       0x3eff6dda9e3100dbULL, 0xb38dc39fdfcc8847ULL, 0x123885528d17b87eULL,
+       0xf2da0ed240b1b642ULL, 0x44cefadcd54bf9a9ULL, 0x1312200e433c7ee6ULL,
+       0x9ffcc84f3a78c748ULL, 0xf0cd1f72248576bbULL, 0xec6974053638cfe4ULL,
+       0x2ba7b67c0cec4e4cULL, 0xac2f4df3e5ce32edULL, 0xcb33d14326ea4c11ULL,
+       0xa4e9044cc77e58bcULL, 0x5f513293d934fcefULL, 0x5dc9645506e55444ULL,
+       0x50de418f317de40aULL, 0x388cb31a69dde259ULL, 0x2db4a83455820a86ULL,
+       0x9010a91e84711ae9ULL, 0x4df7f0b7b1498371ULL, 0xd62a2eabc0977179ULL,
+       0x22fac097aa8d5c0eULL
+};
+
+static const u64 sbox3[256] = {
+       0xf49fcc2ff1daf39bULL, 0x487fd5c66ff29281ULL, 0xe8a30667fcdca83fULL,
+       0x2c9b4be3d2fcce63ULL, 0xda3ff74b93fbbbc2ULL, 0x2fa165d2fe70ba66ULL,
+       0xa103e279970e93d4ULL, 0xbecdec77b0e45e71ULL, 0xcfb41e723985e497ULL,
+       0xb70aaa025ef75017ULL, 0xd42309f03840b8e0ULL, 0x8efc1ad035898579ULL,
+       0x96c6920be2b2abc5ULL, 0x66af4163375a9172ULL, 0x2174abdcca7127fbULL,
+       0xb33ccea64a72ff41ULL, 0xf04a4933083066a5ULL, 0x8d970acdd7289af5ULL,
+       0x8f96e8e031c8c25eULL, 0xf3fec02276875d47ULL, 0xec7bf310056190ddULL,
+       0xf5adb0aebb0f1491ULL, 0x9b50f8850fd58892ULL, 0x4975488358b74de8ULL,
+       0xa3354ff691531c61ULL, 0x0702bbe481d2c6eeULL, 0x89fb24057deded98ULL,
+       0xac3075138596e902ULL, 0x1d2d3580172772edULL, 0xeb738fc28e6bc30dULL,
+       0x5854ef8f63044326ULL, 0x9e5c52325add3bbeULL, 0x90aa53cf325c4623ULL,
+       0xc1d24d51349dd067ULL, 0x2051cfeea69ea624ULL, 0x13220f0a862e7e4fULL,
+       0xce39399404e04864ULL, 0xd9c42ca47086fcb7ULL, 0x685ad2238a03e7ccULL,
+       0x066484b2ab2ff1dbULL, 0xfe9d5d70efbf79ecULL, 0x5b13b9dd9c481854ULL,
+       0x15f0d475ed1509adULL, 0x0bebcd060ec79851ULL, 0xd58c6791183ab7f8ULL,
+       0xd1187c5052f3eee4ULL, 0xc95d1192e54e82ffULL, 0x86eea14cb9ac6ca2ULL,
+       0x3485beb153677d5dULL, 0xdd191d781f8c492aULL, 0xf60866baa784ebf9ULL,
+       0x518f643ba2d08c74ULL, 0x8852e956e1087c22ULL, 0xa768cb8dc410ae8dULL,
+       0x38047726bfec8e1aULL, 0xa67738b4cd3b45aaULL, 0xad16691cec0dde19ULL,
+       0xc6d4319380462e07ULL, 0xc5a5876d0ba61938ULL, 0x16b9fa1fa58fd840ULL,
+       0x188ab1173ca74f18ULL, 0xabda2f98c99c021fULL, 0x3e0580ab134ae816ULL,
+       0x5f3b05b773645abbULL, 0x2501a2be5575f2f6ULL, 0x1b2f74004e7e8ba9ULL,
+       0x1cd7580371e8d953ULL, 0x7f6ed89562764e30ULL, 0xb15926ff596f003dULL,
+       0x9f65293da8c5d6b9ULL, 0x6ecef04dd690f84cULL, 0x4782275fff33af88ULL,
+       0xe41433083f820801ULL, 0xfd0dfe409a1af9b5ULL, 0x4325a3342cdb396bULL,
+       0x8ae77e62b301b252ULL, 0xc36f9e9f6655615aULL, 0x85455a2d92d32c09ULL,
+       0xf2c7dea949477485ULL, 0x63cfb4c133a39ebaULL, 0x83b040cc6ebc5462ULL,
+       0x3b9454c8fdb326b0ULL, 0x56f56a9e87ffd78cULL, 0x2dc2940d99f42bc6ULL,
+       0x98f7df096b096e2dULL, 0x19a6e01e3ad852bfULL, 0x42a99ccbdbd4b40bULL,
+       0xa59998af45e9c559ULL, 0x366295e807d93186ULL, 0x6b48181bfaa1f773ULL,
+       0x1fec57e2157a0a1dULL, 0x4667446af6201ad5ULL, 0xe615ebcacfb0f075ULL,
+       0xb8f31f4f68290778ULL, 0x22713ed6ce22d11eULL, 0x3057c1a72ec3c93bULL,
+       0xcb46acc37c3f1f2fULL, 0xdbb893fd02aaf50eULL, 0x331fd92e600b9fcfULL,
+       0xa498f96148ea3ad6ULL, 0xa8d8426e8b6a83eaULL, 0xa089b274b7735cdcULL,
+       0x87f6b3731e524a11ULL, 0x118808e5cbc96749ULL, 0x9906e4c7b19bd394ULL,
+       0xafed7f7e9b24a20cULL, 0x6509eadeeb3644a7ULL, 0x6c1ef1d3e8ef0edeULL,
+       0xb9c97d43e9798fb4ULL, 0xa2f2d784740c28a3ULL, 0x7b8496476197566fULL,
+       0x7a5be3e6b65f069dULL, 0xf96330ed78be6f10ULL, 0xeee60de77a076a15ULL,
+       0x2b4bee4aa08b9bd0ULL, 0x6a56a63ec7b8894eULL, 0x02121359ba34fef4ULL,
+       0x4cbf99f8283703fcULL, 0x398071350caf30c8ULL, 0xd0a77a89f017687aULL,
+       0xf1c1a9eb9e423569ULL, 0x8c7976282dee8199ULL, 0x5d1737a5dd1f7abdULL,
+       0x4f53433c09a9fa80ULL, 0xfa8b0c53df7ca1d9ULL, 0x3fd9dcbc886ccb77ULL,
+       0xc040917ca91b4720ULL, 0x7dd00142f9d1dcdfULL, 0x8476fc1d4f387b58ULL,
+       0x23f8e7c5f3316503ULL, 0x032a2244e7e37339ULL, 0x5c87a5d750f5a74bULL,
+       0x082b4cc43698992eULL, 0xdf917becb858f63cULL, 0x3270b8fc5bf86ddaULL,
+       0x10ae72bb29b5dd76ULL, 0x576ac94e7700362bULL, 0x1ad112dac61efb8fULL,
+       0x691bc30ec5faa427ULL, 0xff246311cc327143ULL, 0x3142368e30e53206ULL,
+       0x71380e31e02ca396ULL, 0x958d5c960aad76f1ULL, 0xf8d6f430c16da536ULL,
+       0xc8ffd13f1be7e1d2ULL, 0x7578ae66004ddbe1ULL, 0x05833f01067be646ULL,
+       0xbb34b5ad3bfe586dULL, 0x095f34c9a12b97f0ULL, 0x247ab64525d60ca8ULL,
+       0xdcdbc6f3017477d1ULL, 0x4a2e14d4decad24dULL, 0xbdb5e6d9be0a1eebULL,
+       0x2a7e70f7794301abULL, 0xdef42d8a270540fdULL, 0x01078ec0a34c22c1ULL,
+       0xe5de511af4c16387ULL, 0x7ebb3a52bd9a330aULL, 0x77697857aa7d6435ULL,
+       0x004e831603ae4c32ULL, 0xe7a21020ad78e312ULL, 0x9d41a70c6ab420f2ULL,
+       0x28e06c18ea1141e6ULL, 0xd2b28cbd984f6b28ULL, 0x26b75f6c446e9d83ULL,
+       0xba47568c4d418d7fULL, 0xd80badbfe6183d8eULL, 0x0e206d7f5f166044ULL,
+       0xe258a43911cbca3eULL, 0x723a1746b21dc0bcULL, 0xc7caa854f5d7cdd3ULL,
+       0x7cac32883d261d9cULL, 0x7690c26423ba942cULL, 0x17e55524478042b8ULL,
+       0xe0be477656a2389fULL, 0x4d289b5e67ab2da0ULL, 0x44862b9c8fbbfd31ULL,
+       0xb47cc8049d141365ULL, 0x822c1b362b91c793ULL, 0x4eb14655fb13dfd8ULL,
+       0x1ecbba0714e2a97bULL, 0x6143459d5cde5f14ULL, 0x53a8fbf1d5f0ac89ULL,
+       0x97ea04d81c5e5b00ULL, 0x622181a8d4fdb3f3ULL, 0xe9bcd341572a1208ULL,
+       0x1411258643cce58aULL, 0x9144c5fea4c6e0a4ULL, 0x0d33d06565cf620fULL,
+       0x54a48d489f219ca1ULL, 0xc43e5eac6d63c821ULL, 0xa9728b3a72770dafULL,
+       0xd7934e7b20df87efULL, 0xe35503b61a3e86e5ULL, 0xcae321fbc819d504ULL,
+       0x129a50b3ac60bfa6ULL, 0xcd5e68ea7e9fb6c3ULL, 0xb01c90199483b1c7ULL,
+       0x3de93cd5c295376cULL, 0xaed52edf2ab9ad13ULL, 0x2e60f512c0a07884ULL,
+       0xbc3d86a3e36210c9ULL, 0x35269d9b163951ceULL, 0x0c7d6e2ad0cdb5faULL,
+       0x59e86297d87f5733ULL, 0x298ef221898db0e7ULL, 0x55000029d1a5aa7eULL,
+       0x8bc08ae1b5061b45ULL, 0xc2c31c2b6c92703aULL, 0x94cc596baf25ef42ULL,
+       0x0a1d73db22540456ULL, 0x04b6a0f9d9c4179aULL, 0xeffdafa2ae3d3c60ULL,
+       0xf7c8075bb49496c4ULL, 0x9cc5c7141d1cd4e3ULL, 0x78bd1638218e5534ULL,
+       0xb2f11568f850246aULL, 0xedfabcfa9502bc29ULL, 0x796ce5f2da23051bULL,
+       0xaae128b0dc93537cULL, 0x3a493da0ee4b29aeULL, 0xb5df6b2c416895d7ULL,
+       0xfcabbd25122d7f37ULL, 0x70810b58105dc4b1ULL, 0xe10fdd37f7882a90ULL,
+       0x524dcab5518a3f5cULL, 0x3c9e85878451255bULL, 0x4029828119bd34e2ULL,
+       0x74a05b6f5d3ceccbULL, 0xb610021542e13ecaULL, 0x0ff979d12f59e2acULL,
+       0x6037da27e4f9cc50ULL, 0x5e92975a0df1847dULL, 0xd66de190d3e623feULL,
+       0x5032d6b87b568048ULL, 0x9a36b7ce8235216eULL, 0x80272a7a24f64b4aULL,
+       0x93efed8b8c6916f7ULL, 0x37ddbff44cce1555ULL, 0x4b95db5d4b99bd25ULL,
+       0x92d3fda169812fc0ULL, 0xfb1a4a9a90660bb6ULL, 0x730c196946a4b9b2ULL,
+       0x81e289aa7f49da68ULL, 0x64669a0f83b1a05fULL, 0x27b3ff7d9644f48bULL,
+       0xcc6b615c8db675b3ULL, 0x674f20b9bcebbe95ULL, 0x6f31238275655982ULL,
+       0x5ae488713e45cf05ULL, 0xbf619f9954c21157ULL, 0xeabac46040a8eae9ULL,
+       0x454c6fe9f2c0c1cdULL, 0x419cf6496412691cULL, 0xd3dc3bef265b0f70ULL,
+       0x6d0e60f5c3578a9eULL
+};
+
+static const u64 sbox4[256] = {
+       0x5b0e608526323c55ULL, 0x1a46c1a9fa1b59f5ULL, 0xa9e245a17c4c8ffaULL,
+       0x65ca5159db2955d7ULL, 0x05db0a76ce35afc2ULL, 0x81eac77ea9113d45ULL,
+       0x528ef88ab6ac0a0dULL, 0xa09ea253597be3ffULL, 0x430ddfb3ac48cd56ULL,
+       0xc4b3a67af45ce46fULL, 0x4ececfd8fbe2d05eULL, 0x3ef56f10b39935f0ULL,
+       0x0b22d6829cd619c6ULL, 0x17fd460a74df2069ULL, 0x6cf8cc8e8510ed40ULL,
+       0xd6c824bf3a6ecaa7ULL, 0x61243d581a817049ULL, 0x048bacb6bbc163a2ULL,
+       0xd9a38ac27d44cc32ULL, 0x7fddff5baaf410abULL, 0xad6d495aa804824bULL,
+       0xe1a6a74f2d8c9f94ULL, 0xd4f7851235dee8e3ULL, 0xfd4b7f886540d893ULL,
+       0x247c20042aa4bfdaULL, 0x096ea1c517d1327cULL, 0xd56966b4361a6685ULL,
+       0x277da5c31221057dULL, 0x94d59893a43acff7ULL, 0x64f0c51ccdc02281ULL,
+       0x3d33bcc4ff6189dbULL, 0xe005cb184ce66af1ULL, 0xff5ccd1d1db99beaULL,
+       0xb0b854a7fe42980fULL, 0x7bd46a6a718d4b9fULL, 0xd10fa8cc22a5fd8cULL,
+       0xd31484952be4bd31ULL, 0xc7fa975fcb243847ULL, 0x4886ed1e5846c407ULL,
+       0x28cddb791eb70b04ULL, 0xc2b00be2f573417fULL, 0x5c9590452180f877ULL,
+       0x7a6bddfff370eb00ULL, 0xce509e38d6d9d6a4ULL, 0xebeb0f00647fa702ULL,
+       0x1dcc06cf76606f06ULL, 0xe4d9f28ba286ff0aULL, 0xd85a305dc918c262ULL,
+       0x475b1d8732225f54ULL, 0x2d4fb51668ccb5feULL, 0xa679b9d9d72bba20ULL,
+       0x53841c0d912d43a5ULL, 0x3b7eaa48bf12a4e8ULL, 0x781e0e47f22f1ddfULL,
+       0xeff20ce60ab50973ULL, 0x20d261d19dffb742ULL, 0x16a12b03062a2e39ULL,
+       0x1960eb2239650495ULL, 0x251c16fed50eb8b8ULL, 0x9ac0c330f826016eULL,
+       0xed152665953e7671ULL, 0x02d63194a6369570ULL, 0x5074f08394b1c987ULL,
+       0x70ba598c90b25ce1ULL, 0x794a15810b9742f6ULL, 0x0d5925e9fcaf8c6cULL,
+       0x3067716cd868744eULL, 0x910ab077e8d7731bULL, 0x6a61bbdb5ac42f61ULL,
+       0x93513efbf0851567ULL, 0xf494724b9e83e9d5ULL, 0xe887e1985c09648dULL,
+       0x34b1d3c675370cfdULL, 0xdc35e433bc0d255dULL, 0xd0aab84234131be0ULL,
+       0x08042a50b48b7eafULL, 0x9997c4ee44a3ab35ULL, 0x829a7b49201799d0ULL,
+       0x263b8307b7c54441ULL, 0x752f95f4fd6a6ca6ULL, 0x927217402c08c6e5ULL,
+       0x2a8ab754a795d9eeULL, 0xa442f7552f72943dULL, 0x2c31334e19781208ULL,
+       0x4fa98d7ceaee6291ULL, 0x55c3862f665db309ULL, 0xbd0610175d53b1f3ULL,
+       0x46fe6cb840413f27ULL, 0x3fe03792df0cfa59ULL, 0xcfe700372eb85e8fULL,
+       0xa7be29e7adbce118ULL, 0xe544ee5cde8431ddULL, 0x8a781b1b41f1873eULL,
+       0xa5c94c78a0d2f0e7ULL, 0x39412e2877b60728ULL, 0xa1265ef3afc9a62cULL,
+       0xbcc2770c6a2506c5ULL, 0x3ab66dd5dce1ce12ULL, 0xe65499d04a675b37ULL,
+       0x7d8f523481bfd216ULL, 0x0f6f64fcec15f389ULL, 0x74efbe618b5b13c8ULL,
+       0xacdc82b714273e1dULL, 0xdd40bfe003199d17ULL, 0x37e99257e7e061f8ULL,
+       0xfa52626904775aaaULL, 0x8bbbf63a463d56f9ULL, 0xf0013f1543a26e64ULL,
+       0xa8307e9f879ec898ULL, 0xcc4c27a4150177ccULL, 0x1b432f2cca1d3348ULL,
+       0xde1d1f8f9f6fa013ULL, 0x606602a047a7ddd6ULL, 0xd237ab64cc1cb2c7ULL,
+       0x9b938e7225fcd1d3ULL, 0xec4e03708e0ff476ULL, 0xfeb2fbda3d03c12dULL,
+       0xae0bced2ee43889aULL, 0x22cb8923ebfb4f43ULL, 0x69360d013cf7396dULL,
+       0x855e3602d2d4e022ULL, 0x073805bad01f784cULL, 0x33e17a133852f546ULL,
+       0xdf4874058ac7b638ULL, 0xba92b29c678aa14aULL, 0x0ce89fc76cfaadcdULL,
+       0x5f9d4e0908339e34ULL, 0xf1afe9291f5923b9ULL, 0x6e3480f60f4a265fULL,
+       0xeebf3a2ab29b841cULL, 0xe21938a88f91b4adULL, 0x57dfeff845c6d3c3ULL,
+       0x2f006b0bf62caaf2ULL, 0x62f479ef6f75ee78ULL, 0x11a55ad41c8916a9ULL,
+       0xf229d29084fed453ULL, 0x42f1c27b16b000e6ULL, 0x2b1f76749823c074ULL,
+       0x4b76eca3c2745360ULL, 0x8c98f463b91691bdULL, 0x14bcc93cf1ade66aULL,
+       0x8885213e6d458397ULL, 0x8e177df0274d4711ULL, 0xb49b73b5503f2951ULL,
+       0x10168168c3f96b6bULL, 0x0e3d963b63cab0aeULL, 0x8dfc4b5655a1db14ULL,
+       0xf789f1356e14de5cULL, 0x683e68af4e51dac1ULL, 0xc9a84f9d8d4b0fd9ULL,
+       0x3691e03f52a0f9d1ULL, 0x5ed86e46e1878e80ULL, 0x3c711a0e99d07150ULL,
+       0x5a0865b20c4e9310ULL, 0x56fbfc1fe4f0682eULL, 0xea8d5de3105edf9bULL,
+       0x71abfdb12379187aULL, 0x2eb99de1bee77b9cULL, 0x21ecc0ea33cf4523ULL,
+       0x59a4d7521805c7a1ULL, 0x3896f5eb56ae7c72ULL, 0xaa638f3db18f75dcULL,
+       0x9f39358dabe9808eULL, 0xb7defa91c00b72acULL, 0x6b5541fd62492d92ULL,
+       0x6dc6dee8f92e4d5bULL, 0x353f57abc4beea7eULL, 0x735769d6da5690ceULL,
+       0x0a234aa642391484ULL, 0xf6f9508028f80d9dULL, 0xb8e319a27ab3f215ULL,
+       0x31ad9c1151341a4dULL, 0x773c22a57bef5805ULL, 0x45c7561a07968633ULL,
+       0xf913da9e249dbe36ULL, 0xda652d9b78a64c68ULL, 0x4c27a97f3bc334efULL,
+       0x76621220e66b17f4ULL, 0x967743899acd7d0bULL, 0xf3ee5bcae0ed6782ULL,
+       0x409f753600c879fcULL, 0x06d09a39b5926db6ULL, 0x6f83aeb0317ac588ULL,
+       0x01e6ca4a86381f21ULL, 0x66ff3462d19f3025ULL, 0x72207c24ddfd3bfbULL,
+       0x4af6b6d3e2ece2ebULL, 0x9c994dbec7ea08deULL, 0x49ace597b09a8bc4ULL,
+       0xb38c4766cf0797baULL, 0x131b9373c57c2a75ULL, 0xb1822cce61931e58ULL,
+       0x9d7555b909ba1c0cULL, 0x127fafdd937d11d2ULL, 0x29da3badc66d92e4ULL,
+       0xa2c1d57154c2ecbcULL, 0x58c5134d82f6fe24ULL, 0x1c3ae3515b62274fULL,
+       0xe907c82e01cb8126ULL, 0xf8ed091913e37fcbULL, 0x3249d8f9c80046c9ULL,
+       0x80cf9bede388fb63ULL, 0x1881539a116cf19eULL, 0x5103f3f76bd52457ULL,
+       0x15b7e6f5ae47f7a8ULL, 0xdbd7c6ded47e9ccfULL, 0x44e55c410228bb1aULL,
+       0xb647d4255edb4e99ULL, 0x5d11882bb8aafc30ULL, 0xf5098bbb29d3212aULL,
+       0x8fb5ea14e90296b3ULL, 0x677b942157dd025aULL, 0xfb58e7c0a390acb5ULL,
+       0x89d3674c83bd4a01ULL, 0x9e2da4df4bf3b93bULL, 0xfcc41e328cab4829ULL,
+       0x03f38c96ba582c52ULL, 0xcad1bdbd7fd85db2ULL, 0xbbb442c16082ae83ULL,
+       0xb95fe86ba5da9ab0ULL, 0xb22e04673771a93fULL, 0x845358c9493152d8ULL,
+       0xbe2a488697b4541eULL, 0x95a2dc2dd38e6966ULL, 0xc02c11ac923c852bULL,
+       0x2388b1990df2a87bULL, 0x7c8008fa1b4f37beULL, 0x1f70d0c84d54e503ULL,
+       0x5490adec7ece57d4ULL, 0x002b3c27d9063a3aULL, 0x7eaea3848030a2bfULL,
+       0xc602326ded2003c0ULL, 0x83a7287d69a94086ULL, 0xc57a5fcb30f57a8aULL,
+       0xb56844e479ebe779ULL, 0xa373b40f05dcbce9ULL, 0xd71a786e88570ee2ULL,
+       0x879cbacdbde8f6a0ULL, 0x976ad1bcc164a32fULL, 0xab21e25e9666d78bULL,
+       0x901063aae5e5c33cULL, 0x9818b34448698d90ULL, 0xe36487ae3e1e8abbULL,
+       0xafbdf931893bdcb4ULL, 0x6345a0dc5fbbd519ULL, 0x8628fe269b9465caULL,
+       0x1e5d01603f9c51ecULL, 0x4de44006a15049b7ULL, 0xbf6c70e5f776cbb1ULL,
+       0x411218f2ef552bedULL, 0xcb0c0708705a36a3ULL, 0xe74d14754f986044ULL,
+       0xcd56d9430ea8280eULL, 0xc12591d7535f5065ULL, 0xc83223f1720aef96ULL,
+       0xc3a0396f7363a51fULL
+};
+
+
+static void tgr192_round(u64 * ra, u64 * rb, u64 * rc, u64 x, int mul)
+{
+       u64 a = *ra;
+       u64 b = *rb;
+       u64 c = *rc;
+
+       c ^= x;
+       a -= sbox1[c         & 0xff] ^ sbox2[(c >> 16) & 0xff]
+          ^ sbox3[(c >> 32) & 0xff] ^ sbox4[(c >> 48) & 0xff];
+       b += sbox4[(c >>  8) & 0xff] ^ sbox3[(c >> 24) & 0xff]
+          ^ sbox2[(c >> 40) & 0xff] ^ sbox1[(c >> 56) & 0xff];
+       b *= mul;
+
+       *ra = a;
+       *rb = b;
+       *rc = c;
+}
+
+
+static void tgr192_pass(u64 * ra, u64 * rb, u64 * rc, u64 * x, int mul)
+{
+       u64 a = *ra;
+       u64 b = *rb;
+       u64 c = *rc;
+
+       tgr192_round(&a, &b, &c, x[0], mul);
+       tgr192_round(&b, &c, &a, x[1], mul);
+       tgr192_round(&c, &a, &b, x[2], mul);
+       tgr192_round(&a, &b, &c, x[3], mul);
+       tgr192_round(&b, &c, &a, x[4], mul);
+       tgr192_round(&c, &a, &b, x[5], mul);
+       tgr192_round(&a, &b, &c, x[6], mul);
+       tgr192_round(&b, &c, &a, x[7], mul);
+
+       *ra = a;
+       *rb = b;
+       *rc = c;
+}
+
+
+static void tgr192_key_schedule(u64 * x)
+{
+       x[0] -= x[7] ^ 0xa5a5a5a5a5a5a5a5ULL;
+       x[1] ^= x[0];
+       x[2] += x[1];
+       x[3] -= x[2] ^ ((~x[1]) << 19);
+       x[4] ^= x[3];
+       x[5] += x[4];
+       x[6] -= x[5] ^ ((~x[4]) >> 23);
+       x[7] ^= x[6];
+       x[0] += x[7];
+       x[1] -= x[0] ^ ((~x[7]) << 19);
+       x[2] ^= x[1];
+       x[3] += x[2];
+       x[4] -= x[3] ^ ((~x[2]) >> 23);
+       x[5] ^= x[4];
+       x[6] += x[5];
+       x[7] -= x[6] ^ 0x0123456789abcdefULL;
+}
+
+
+/****************
+ * Transform the message DATA which consists of 512 bytes (8 words)
+ */
+
+static void tgr192_transform(struct tgr192_ctx *tctx, const u8 * data)
+{
+       u64 a, b, c, aa, bb, cc;
+       u64 x[8];
+       int i;
+       const u8 *ptr = data;
+
+       for (i = 0; i < 8; i++, ptr += 8) {
+               x[i] = (((u64)ptr[7] ) << 56) ^
+               (((u64)ptr[6] & 0xffL) << 48) ^
+               (((u64)ptr[5] & 0xffL) << 40) ^
+               (((u64)ptr[4] & 0xffL) << 32) ^
+               (((u64)ptr[3] & 0xffL) << 24) ^
+               (((u64)ptr[2] & 0xffL) << 16) ^
+               (((u64)ptr[1] & 0xffL) <<  8) ^
+               (((u64)ptr[0] & 0xffL)      );
+       }
+
+       /* save */
+       a = aa = tctx->a;
+       b = bb = tctx->b;
+       c = cc = tctx->c;
+
+       tgr192_pass(&a, &b, &c, x, 5);
+       tgr192_key_schedule(x);
+       tgr192_pass(&c, &a, &b, x, 7);
+       tgr192_key_schedule(x);
+       tgr192_pass(&b, &c, &a, x, 9);
+
+
+       /* feedforward */
+       a ^= aa;
+       b -= bb;
+       c += cc;
+       /* store */
+       tctx->a = a;
+       tctx->b = b;
+       tctx->c = c;
+}
+
+static void tgr192_init(void *ctx)
+{
+       struct tgr192_ctx *tctx = ctx;
+
+       memset (tctx->hash, 0, 64);
+       tctx->a = 0x0123456789abcdefULL;
+       tctx->b = 0xfedcba9876543210ULL;
+       tctx->c = 0xf096a5b4c3b2e187ULL;
+       tctx->nblocks = 0;
+       tctx->count = 0;
+}
+
+
+/* Update the message digest with the contents
+ * of INBUF with length INLEN. */
+static void tgr192_update(void *ctx, const u8 * inbuf, unsigned int len)
+{
+       struct tgr192_ctx *tctx = ctx;
+
+       if (tctx->count == 64) {        /* flush the buffer */
+               tgr192_transform(tctx, tctx->hash);
+               tctx->count = 0;
+               tctx->nblocks++;
+       }
+       if (!inbuf) {
+               return;
+       }
+       if (tctx->count) {
+               for (; len && tctx->count < 64; len--) {
+                       tctx->hash[tctx->count++] = *inbuf++;
+               }
+               tgr192_update(tctx, NULL, 0);
+               if (!len) {
+                       return;
+               }
+
+       }
+
+       while (len >= 64) {
+               tgr192_transform(tctx, inbuf);
+               tctx->count = 0;
+               tctx->nblocks++;
+               len -= 64;
+               inbuf += 64;
+       }
+       for (; len && tctx->count < 64; len--) {
+               tctx->hash[tctx->count++] = *inbuf++;
+       }
+}
+
+
+
+/* The routine terminates the computation */
+static void tgr192_final(void *ctx, u8 * out)
+{
+       struct tgr192_ctx *tctx = ctx;
+       u32 t, msb, lsb;
+       u8 *p;
+       int i, j;
+
+       tgr192_update(tctx, NULL, 0); /* flush */ ;
+
+       msb = 0;
+       t = tctx->nblocks;
+       if ((lsb = t << 6) < t) { /* multiply by 64 to make a byte count */
+               msb++;
+       }
+       msb += t >> 26;
+       t = lsb;
+       if ((lsb = t + tctx->count) < t) {      /* add the count */
+               msb++;
+       }
+       t = lsb;
+       if ((lsb = t << 3) < t) { /* multiply by 8 to make a bit count */
+               msb++;
+       }
+       msb += t >> 29;
+
+       if (tctx->count < 56) { /* enough room */
+               tctx->hash[tctx->count++] = 0x01;       /* pad */
+               while (tctx->count < 56) {
+                       tctx->hash[tctx->count++] = 0;  /* pad */
+               }
+       } else {                /* need one extra block */
+               tctx->hash[tctx->count++] = 0x01;       /* pad character */
+               while (tctx->count < 64) {
+                       tctx->hash[tctx->count++] = 0;
+               }
+               tgr192_update(tctx, NULL, 0); /* flush */ ;
+               memset(tctx->hash, 0, 56);    /* fill next block with zeroes */
+       }
+       /* append the 64 bit count */
+       tctx->hash[56] = lsb;
+       tctx->hash[57] = lsb >> 8;
+       tctx->hash[58] = lsb >> 16;
+       tctx->hash[59] = lsb >> 24;
+       tctx->hash[60] = msb;
+       tctx->hash[61] = msb >> 8;
+       tctx->hash[62] = msb >> 16;
+       tctx->hash[63] = msb >> 24;
+       tgr192_transform(tctx, tctx->hash);
+
+       p = tctx->hash;
+       *p++ = tctx->a >> 56; *p++ = tctx->a >> 48; *p++ = tctx->a >> 40;
+       *p++ = tctx->a >> 32; *p++ = tctx->a >> 24; *p++ = tctx->a >> 16;
+       *p++ = tctx->a >>  8; *p++ = tctx->a;\
+       *p++ = tctx->b >> 56; *p++ = tctx->b >> 48; *p++ = tctx->b >> 40;
+       *p++ = tctx->b >> 32; *p++ = tctx->b >> 24; *p++ = tctx->b >> 16;
+       *p++ = tctx->b >>  8; *p++ = tctx->b;
+       *p++ = tctx->c >> 56; *p++ = tctx->c >> 48; *p++ = tctx->c >> 40;
+       *p++ = tctx->c >> 32; *p++ = tctx->c >> 24; *p++ = tctx->c >> 16;
+       *p++ = tctx->c >>  8; *p++ = tctx->c;
+
+
+       /* unpack the hash */
+       j = 7;
+       for (i = 0; i < 8; i++) {
+               out[j--] = (tctx->a >> 8 * i) & 0xff;
+       }
+       j = 15;
+       for (i = 0; i < 8; i++) {
+               out[j--] = (tctx->b >> 8 * i) & 0xff;
+       }
+       j = 23;
+       for (i = 0; i < 8; i++) {
+               out[j--] = (tctx->c >> 8 * i) & 0xff;
+       }
+}
+
+static void tgr160_final(void *ctx, u8 * out)
+{
+       struct tgr192_ctx *wctx = ctx;
+       u8 D[64];
+
+       tgr192_final(wctx, D);
+       memcpy(out, D, TGR160_DIGEST_SIZE);
+       memset(D, 0, TGR192_DIGEST_SIZE);
+}
+
+static void tgr128_final(void *ctx, u8 * out)
+{
+       struct tgr192_ctx *wctx = ctx;
+       u8 D[64];
+
+       tgr192_final(wctx, D);
+       memcpy(out, D, TGR128_DIGEST_SIZE);
+       memset(D, 0, TGR192_DIGEST_SIZE);
+}
+
+static struct crypto_alg tgr192 = {
+       .cra_name = "tgr192",
+       .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
+       .cra_blocksize = TGR192_BLOCK_SIZE,
+       .cra_ctxsize = sizeof(struct tgr192_ctx),
+       .cra_module = THIS_MODULE,
+       .cra_list = LIST_HEAD_INIT(tgr192.cra_list),
+       .cra_u = {.digest = {
+                            .dia_digestsize = TGR192_DIGEST_SIZE,
+                            .dia_init = tgr192_init,
+                            .dia_update = tgr192_update,
+                            .dia_final = tgr192_final}}
+};
+
+static struct crypto_alg tgr160 = {
+       .cra_name = "tgr160",
+       .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
+       .cra_blocksize = TGR192_BLOCK_SIZE,
+       .cra_ctxsize = sizeof(struct tgr192_ctx),
+       .cra_module = THIS_MODULE,
+       .cra_list = LIST_HEAD_INIT(tgr160.cra_list),
+       .cra_u = {.digest = {
+                            .dia_digestsize = TGR160_DIGEST_SIZE,
+                            .dia_init = tgr192_init,
+                            .dia_update = tgr192_update,
+                            .dia_final = tgr160_final}}
+};
+
+static struct crypto_alg tgr128 = {
+       .cra_name = "tgr128",
+       .cra_flags = CRYPTO_ALG_TYPE_DIGEST,
+       .cra_blocksize = TGR192_BLOCK_SIZE,
+       .cra_ctxsize = sizeof(struct tgr192_ctx),
+       .cra_module = THIS_MODULE,
+       .cra_list = LIST_HEAD_INIT(tgr128.cra_list),
+       .cra_u = {.digest = {
+                            .dia_digestsize = TGR128_DIGEST_SIZE,
+                            .dia_init = tgr192_init,
+                            .dia_update = tgr192_update,
+                            .dia_final = tgr128_final}}
+};
+
+static int __init init(void)
+{
+       int ret = 0;
+
+       ret = crypto_register_alg(&tgr192);
+
+       if (ret < 0) {
+               goto out;
+       }
+
+       ret = crypto_register_alg(&tgr160);
+       if (ret < 0) {
+               crypto_unregister_alg(&tgr192);
+               goto out;
+       }
+
+       ret = crypto_register_alg(&tgr128);
+       if (ret < 0) {
+               crypto_unregister_alg(&tgr192);
+               crypto_unregister_alg(&tgr160);
+       }
+      out:
+       return ret;
+}
+
+static void __exit fini(void)
+{
+       crypto_unregister_alg(&tgr192);
+       crypto_unregister_alg(&tgr160);
+       crypto_unregister_alg(&tgr128);
+}
+
+MODULE_ALIAS("tgr160");
+MODULE_ALIAS("tgr128");
+
+module_init(init);
+module_exit(fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Tiger Message Digest Algorithm");
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c
new file mode 100644 (file)
index 0000000..77285ff
--- /dev/null
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2004 Intel Corporation <naveen.b.s@intel.com>
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * ACPI based HotPlug driver that supports Memory Hotplug
+ * This driver fields notifications from firmare for memory add
+ * and remove operations and alerts the VM of the affected memory
+ * ranges.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/memory_hotplug.h>
+#include <acpi/acpi_drivers.h>
+
+
+#define ACPI_MEMORY_DEVICE_COMPONENT           0x08000000UL
+#define ACPI_MEMORY_DEVICE_CLASS               "memory"
+#define ACPI_MEMORY_DEVICE_HID                 "PNP0C80"
+#define ACPI_MEMORY_DEVICE_DRIVER_NAME         "Hotplug Mem Driver"
+#define ACPI_MEMORY_DEVICE_NAME                        "Hotplug Mem Device"
+
+#define _COMPONENT             ACPI_MEMORY_DEVICE_COMPONENT
+
+ACPI_MODULE_NAME               ("acpi_memory")
+MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>");
+MODULE_DESCRIPTION(ACPI_MEMORY_DEVICE_DRIVER_NAME);
+MODULE_LICENSE("GPL");
+
+/* ACPI _STA method values */
+#define ACPI_MEMORY_STA_PRESENT                (0x00000001UL)
+#define ACPI_MEMORY_STA_ENABLED                (0x00000002UL)
+#define ACPI_MEMORY_STA_FUNCTIONAL     (0x00000008UL)
+
+/* Memory Device States */
+#define MEMORY_INVALID_STATE   0
+#define MEMORY_POWER_ON_STATE  1
+#define MEMORY_POWER_OFF_STATE 2
+
+static int acpi_memory_device_add (struct acpi_device *device);
+static int acpi_memory_device_remove (struct acpi_device *device, int type);
+
+static struct acpi_driver acpi_memory_device_driver = {
+       .name =         ACPI_MEMORY_DEVICE_DRIVER_NAME,
+       .class =        ACPI_MEMORY_DEVICE_CLASS,
+       .ids =          ACPI_MEMORY_DEVICE_HID,
+       .ops =          {
+                               .add =          acpi_memory_device_add,
+                               .remove =       acpi_memory_device_remove,
+                       },
+};
+
+struct acpi_memory_device {
+       acpi_handle handle;
+       unsigned int state;             /* State of the memory device */
+       unsigned short cache_attribute; /* memory cache attribute */
+       unsigned short read_write_attribute;/* memory read/write attribute */
+       u64 start_addr; /* Memory Range start physical addr */
+       u64 end_addr;   /* Memory Range end physical addr */
+};
+
+
+static int
+acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
+{
+       acpi_status status;
+       struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+       struct acpi_resource *resource = NULL;
+       struct acpi_resource_address64 address64;
+
+       ACPI_FUNCTION_TRACE("acpi_memory_get_device_resources");
+
+       /* Get the range from the _CRS */
+       status = acpi_get_current_resources(mem_device->handle, &buffer);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-EINVAL);
+
+       resource = (struct acpi_resource *) buffer.pointer;
+       status = acpi_resource_to_address64(resource, &address64);
+       if (ACPI_SUCCESS(status)) {
+               if (address64.resource_type == ACPI_MEMORY_RANGE) {
+                       /* Populate the structure */
+                       mem_device->cache_attribute =
+                               address64.attribute.memory.cache_attribute;
+                       mem_device->read_write_attribute =
+                       address64.attribute.memory.read_write_attribute;
+                       mem_device->start_addr = address64.min_address_range;
+                       mem_device->end_addr = address64.max_address_range;
+               }
+       }
+
+       acpi_os_free(buffer.pointer);
+       return_VALUE(0);
+}
+
+static int
+acpi_memory_get_device(acpi_handle handle,
+       struct acpi_memory_device **mem_device)
+{
+       acpi_status status;
+       acpi_handle phandle;
+       struct acpi_device *device = NULL;
+       struct acpi_device *pdevice = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_memory_get_device");
+
+       if (!acpi_bus_get_device(handle, &device) && device)
+               goto end;
+
+       status = acpi_get_parent(handle, &phandle);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error in acpi_get_parent\n"));
+               return_VALUE(-EINVAL);
+       }
+
+       /* Get the parent device */
+       status = acpi_bus_get_device(phandle, &pdevice);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error in acpi_bus_get_device\n"));
+               return_VALUE(-EINVAL);
+       }
+
+       /*
+        * Now add the notified device.  This creates the acpi_device
+        * and invokes .add function
+        */
+       status = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error in acpi_bus_add\n"));
+               return_VALUE(-EINVAL);
+       }
+
+end:
+       *mem_device = acpi_driver_data(device);
+       if (!(*mem_device)) {
+               printk(KERN_ERR "\n driver data not found" );
+               return_VALUE(-ENODEV);
+       }
+
+       return_VALUE(0);
+}
+
+static int
+acpi_memory_check_device(struct acpi_memory_device *mem_device)
+{
+       unsigned long current_status;
+
+       ACPI_FUNCTION_TRACE("acpi_memory_check_device");
+
+       /* Get device present/absent information from the _STA */
+       if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->handle, "_STA",
+               NULL, &current_status)))
+               return_VALUE(-ENODEV);
+       /*
+        * Check for device status. Device should be
+        * present/enabled/functioning.
+        */
+       if (!((current_status & ACPI_MEMORY_STA_PRESENT)
+               && (current_status & ACPI_MEMORY_STA_ENABLED)
+               && (current_status & ACPI_MEMORY_STA_FUNCTIONAL)))
+               return_VALUE(-ENODEV);
+
+       return_VALUE(0);
+}
+
+static int
+acpi_memory_enable_device(struct acpi_memory_device *mem_device)
+{
+       int result;
+
+       ACPI_FUNCTION_TRACE("acpi_memory_enable_device");
+
+       /* Get the range from the _CRS */
+       result = acpi_memory_get_device_resources(mem_device);
+       if (result) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "\nget_device_resources failed\n"));
+               mem_device->state = MEMORY_INVALID_STATE;
+               return result;
+       }
+
+       /*
+        * Tell the VM there is more memory here...
+        * Note: Assume that this function returns zero on success
+        */
+       result = add_memory(mem_device->start_addr,
+                       (mem_device->end_addr - mem_device->start_addr) + 1,
+                       mem_device->read_write_attribute);
+       if (result) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "\nadd_memory failed\n"));
+               mem_device->state = MEMORY_INVALID_STATE;
+               return result;
+       }
+
+       return result;
+}
+
+static int
+acpi_memory_powerdown_device(struct acpi_memory_device *mem_device)
+{
+       acpi_status status;
+       struct acpi_object_list arg_list;
+       union acpi_object arg;
+       unsigned long current_status;
+
+       ACPI_FUNCTION_TRACE("acpi_memory_powerdown_device");
+
+       /* Issue the _EJ0 command */
+       arg_list.count = 1;
+       arg_list.pointer = &arg;
+       arg.type = ACPI_TYPE_INTEGER;
+       arg.integer.value = 1;
+       status = acpi_evaluate_object(mem_device->handle,
+                       "_EJ0", &arg_list, NULL);
+       /* Return on _EJ0 failure */
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"_EJ0 failed.\n"));
+               return_VALUE(-ENODEV);
+       }
+
+       /* Evalute _STA to check if the device is disabled */
+       status = acpi_evaluate_integer(mem_device->handle, "_STA",
+               NULL, &current_status);
+       if (ACPI_FAILURE(status))
+               return_VALUE(-ENODEV);
+
+       /* Check for device status.  Device should be disabled */
+       if (current_status & ACPI_MEMORY_STA_ENABLED)
+               return_VALUE(-EINVAL);
+
+       return_VALUE(0);
+}
+
+static int
+acpi_memory_disable_device(struct acpi_memory_device *mem_device)
+{
+       int result;
+       u64 start = mem_device->start_addr;
+       u64 len = mem_device->end_addr - start + 1;
+       unsigned long attr = mem_device->read_write_attribute;
+
+       ACPI_FUNCTION_TRACE("acpi_memory_disable_device");
+
+       /*
+        * Ask the VM to offline this memory range.
+        * Note: Assume that this function returns zero on success
+        */
+       result = remove_memory(start, len, attr);
+       if (result) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Hot-Remove failed.\n"));
+               return_VALUE(result);
+       }
+
+       /* Power-off and eject the device */
+       result = acpi_memory_powerdown_device(mem_device);
+       if (result) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                       "Device Power Down failed.\n"));
+               /* Set the status of the device to invalid */
+               mem_device->state = MEMORY_INVALID_STATE;
+               return result;
+       }
+
+       mem_device->state = MEMORY_POWER_OFF_STATE;
+       return result;
+}
+
+static void
+acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
+{
+       struct acpi_memory_device *mem_device;
+       struct acpi_device *device;
+
+       ACPI_FUNCTION_TRACE("acpi_memory_device_notify");
+
+       switch (event) {
+       case ACPI_NOTIFY_BUS_CHECK:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "\nReceived BUS CHECK notification for device\n"));
+               /* Fall Through */
+       case ACPI_NOTIFY_DEVICE_CHECK:
+               if (event == ACPI_NOTIFY_DEVICE_CHECK)
+                       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "\nReceived DEVICE CHECK notification for device\n"));
+               if (acpi_memory_get_device(handle, &mem_device)) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                               "Error in finding driver data\n"));
+                       return_VOID;
+               }
+
+               if (!acpi_memory_check_device(mem_device)) {
+                       if (acpi_memory_enable_device(mem_device))
+                               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                               "Error in acpi_memory_enable_device\n"));
+               }
+               break;
+       case ACPI_NOTIFY_EJECT_REQUEST:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "\nReceived EJECT REQUEST notification for device\n"));
+
+               if (acpi_bus_get_device(handle, &device)) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                       "Device doesn't exist\n"));
+                       break;
+               }
+               mem_device = acpi_driver_data(device);
+               if (!mem_device) {
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                       "Driver Data is NULL\n"));
+                       break;
+               }
+
+               /*
+                * Currently disabling memory device from kernel mode
+                * TBD: Can also be disabled from user mode scripts
+                * TBD: Can also be disabled by Callback registration
+                *      with generic sysfs driver
+                */
+               if (acpi_memory_disable_device(mem_device))
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                               "Error in acpi_memory_disable_device\n"));
+               /*
+                * TBD: Invoke acpi_bus_remove to cleanup data structures
+                */
+               break;
+       default:
+               ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                       "Unsupported event [0x%x]\n", event));
+               break;
+       }
+
+       return_VOID;
+}
+
+static int
+acpi_memory_device_add(struct acpi_device *device)
+{
+       int result;
+       struct acpi_memory_device *mem_device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_memory_device_add");
+
+       if (!device)
+               return_VALUE(-EINVAL);
+
+       mem_device = kmalloc(sizeof(struct acpi_memory_device), GFP_KERNEL);
+       if (!mem_device)
+               return_VALUE(-ENOMEM);
+       memset(mem_device, 0, sizeof(struct acpi_memory_device));
+
+       mem_device->handle = device->handle;
+       sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
+       sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
+       acpi_driver_data(device) = mem_device;
+
+       /* Get the range from the _CRS */
+       result = acpi_memory_get_device_resources(mem_device);
+       if (result) {
+               kfree(mem_device);
+               return_VALUE(result);
+       }
+
+       /* Set the device state */
+       mem_device->state = MEMORY_POWER_ON_STATE;
+
+       printk(KERN_INFO "%s \n", acpi_device_name(device));
+
+       return_VALUE(result);
+}
+
+static int
+acpi_memory_device_remove (struct acpi_device *device, int type)
+{
+       struct acpi_memory_device *mem_device = NULL;
+
+       ACPI_FUNCTION_TRACE("acpi_memory_device_remove");
+
+       if (!device || !acpi_driver_data(device))
+               return_VALUE(-EINVAL);
+
+       mem_device = (struct acpi_memory_device *) acpi_driver_data(device);
+       kfree(mem_device);
+
+       return_VALUE(0);
+}
+
+/*
+ * Helper function to check for memory device
+ */
+static acpi_status
+is_memory_device(acpi_handle handle)
+{
+       char *hardware_id;
+       acpi_status status;
+       struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+       struct acpi_device_info *info;
+
+       ACPI_FUNCTION_TRACE("is_memory_device");
+
+       status = acpi_get_object_info(handle, &buffer);
+       if (ACPI_FAILURE(status))
+               return_ACPI_STATUS(AE_ERROR);
+
+       info = buffer.pointer;
+       if (!(info->valid & ACPI_VALID_HID)) {
+               acpi_os_free(buffer.pointer);
+               return_ACPI_STATUS(AE_ERROR);
+       }
+
+       hardware_id = info->hardware_id.value;
+       if ((hardware_id == NULL) ||
+               (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID)))
+               status = AE_ERROR;
+
+       acpi_os_free(buffer.pointer);
+       return_ACPI_STATUS(status);
+}
+
+static acpi_status
+acpi_memory_register_notify_handler (acpi_handle handle,
+       u32 level, void *ctxt, void **retv)
+{
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE("acpi_memory_register_notify_handler");
+
+       status = is_memory_device(handle);
+       if (ACPI_FAILURE(status))
+               return_ACPI_STATUS(AE_OK);      /* continue */
+
+       status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+                       acpi_memory_device_notify, NULL);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                       "Error installing notify handler\n"));
+               return_ACPI_STATUS(AE_OK);      /* continue */
+       }
+
+       return_ACPI_STATUS(status);
+}
+
+static acpi_status
+acpi_memory_deregister_notify_handler (acpi_handle handle,
+                              u32 level, void *ctxt, void **retv)
+{
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE("acpi_memory_deregister_notify_handler");
+
+       status = is_memory_device(handle);
+       if (ACPI_FAILURE(status))
+               return_ACPI_STATUS(AE_OK);      /* continue */
+
+       status = acpi_remove_notify_handler(handle,
+                       ACPI_SYSTEM_NOTIFY, acpi_memory_device_notify);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                               "Error removing notify handler\n"));
+               return_ACPI_STATUS(AE_OK);      /* continue */
+       }
+
+       return_ACPI_STATUS(status);
+}
+
+static int __init
+acpi_memory_device_init (void)
+{
+       int result;
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE("acpi_memory_device_init");
+
+       result = acpi_bus_register_driver(&acpi_memory_device_driver);
+
+       if (result < 0)
+               return_VALUE(-ENODEV);
+
+       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+                               ACPI_UINT32_MAX,
+                               acpi_memory_register_notify_handler,
+                               NULL, NULL);
+
+       if (ACPI_FAILURE (status)) {
+               ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "walk_namespace failed\n"));
+               acpi_bus_unregister_driver(&acpi_memory_device_driver);
+               return_VALUE(-ENODEV);
+        }
+
+       return_VALUE(0);
+}
+
+static void __exit
+acpi_memory_device_exit (void)
+{
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE("acpi_memory_device_exit");
+
+       /*
+        * Adding this to un-install notification handlers for all the device
+        * handles.
+        */
+       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+                       ACPI_UINT32_MAX,
+                       acpi_memory_deregister_notify_handler,
+                       NULL, NULL);
+
+       if (ACPI_FAILURE (status))
+               ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "walk_namespace failed\n"));
+
+       acpi_bus_unregister_driver(&acpi_memory_device_driver);
+
+       return_VOID;
+}
+
+module_init(acpi_memory_device_init);
+module_exit(acpi_memory_device_exit);
+
+
diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c
new file mode 100644 (file)
index 0000000..d3aa159
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * 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-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+/*
+ * SGI TIOCA AGPGART routines.
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/pcidev.h>
+#include <asm/sn/pcibus_provider_defs.h>
+#include <asm/sn/tioca_provider.h>
+#include "agp.h"
+
+extern int agp_memory_reserved;
+extern uint32_t tioca_gart_found;
+extern struct list_head tioca_list;
+static struct agp_bridge_data **sgi_tioca_agp_bridges;
+
+/*
+ * The aperature size and related information is set up at TIOCA init time.
+ * Values for this table will be extracted and filled in at
+ * sgi_tioca_fetch_size() time.
+ */
+
+static struct aper_size_info_fixed sgi_tioca_sizes[] = {
+       {0, 0, 0},
+};
+
+static void *sgi_tioca_alloc_page(struct agp_bridge_data *bridge)
+{
+       struct page *page;
+       int nid;
+       struct tioca_kernel *info =
+           (struct tioca_kernel *)bridge->dev_private_data;
+
+       nid = info->ca_closest_node;
+       page = alloc_pages_node(nid, GFP_KERNEL, 0);
+       if (page == NULL) {
+               return 0;
+       }
+
+       get_page(page);
+       SetPageLocked(page);
+       atomic_inc(&agp_bridge->current_memory_agp);
+       return page_address(page);
+}
+
+/*
+ * Flush GART tlb's.  Cannot selectively flush based on memory so the mem
+ * arg is ignored.
+ */
+
+static void sgi_tioca_tlbflush(struct agp_memory *mem)
+{
+       tioca_tlbflush(mem->bridge->dev_private_data);
+}
+
+/*
+ * Given an address of a host physical page, turn it into a valid gart
+ * entry.
+ */
+static unsigned long
+sgi_tioca_mask_memory(struct agp_bridge_data *bridge,
+                     unsigned long addr, int type)
+{
+       return tioca_physpage_to_gart(addr);
+}
+
+static void sgi_tioca_agp_enable(struct agp_bridge_data *bridge, u32 mode)
+{
+       tioca_fastwrite_enable(bridge->dev_private_data);
+}
+
+/*
+ * sgi_tioca_configure() doesn't have anything to do since the base CA driver
+ * has alreay set up the GART.
+ */
+
+static int sgi_tioca_configure(void)
+{
+       return 0;
+}
+
+/*
+ * Determine gfx aperature size.  This has already been determined by the
+ * CA driver init, so just need to set agp_bridge values accordingly.
+ */
+
+static int sgi_tioca_fetch_size(void)
+{
+       struct tioca_kernel *info =
+           (struct tioca_kernel *)agp_bridge->dev_private_data;
+
+       sgi_tioca_sizes[0].size = info->ca_gfxap_size / MB(1);
+       sgi_tioca_sizes[0].num_entries = info->ca_gfxgart_entries;
+
+       return sgi_tioca_sizes[0].size;
+}
+
+static int sgi_tioca_create_gatt_table(struct agp_bridge_data *bridge)
+{
+       struct tioca_kernel *info =
+           (struct tioca_kernel *)bridge->dev_private_data;
+
+       bridge->gatt_table_real = (u32 *) info->ca_gfxgart;
+       bridge->gatt_table = bridge->gatt_table_real;
+       bridge->gatt_bus_addr = info->ca_gfxgart_base;
+
+       return 0;
+}
+
+static int sgi_tioca_free_gatt_table(struct agp_bridge_data *bridge)
+{
+       return 0;
+}
+
+static int sgi_tioca_insert_memory(struct agp_memory *mem, off_t pg_start,
+                                  int type)
+{
+       int num_entries;
+       size_t i;
+       off_t j;
+       void *temp;
+       struct agp_bridge_data *bridge;
+       u64 *table;
+
+       bridge = mem->bridge;
+       if (!bridge)
+               return -EINVAL;
+
+       table = (u64 *)bridge->gatt_table;
+
+       temp = bridge->current_size;
+
+       switch (bridge->driver->size_type) {
+       case U8_APER_SIZE:
+               num_entries = A_SIZE_8(temp)->num_entries;
+               break;
+       case U16_APER_SIZE:
+               num_entries = A_SIZE_16(temp)->num_entries;
+               break;
+       case U32_APER_SIZE:
+               num_entries = A_SIZE_32(temp)->num_entries;
+               break;
+       case FIXED_APER_SIZE:
+               num_entries = A_SIZE_FIX(temp)->num_entries;
+               break;
+       case LVL2_APER_SIZE:
+               return -EINVAL;
+               break;
+       default:
+               num_entries = 0;
+               break;
+       }
+
+       num_entries -= agp_memory_reserved / PAGE_SIZE;
+       if (num_entries < 0)
+               num_entries = 0;
+
+       if (type != 0 || mem->type != 0) {
+               return -EINVAL;
+       }
+
+       if ((pg_start + mem->page_count) > num_entries)
+               return -EINVAL;
+
+       j = pg_start;
+
+       while (j < (pg_start + mem->page_count)) {
+               if (table[j])
+                       return -EBUSY;
+               j++;
+       }
+
+       if (mem->is_flushed == FALSE) {
+               bridge->driver->cache_flush();
+               mem->is_flushed = TRUE;
+       }
+
+       for (i = 0, j = pg_start; i < mem->page_count; i++, j++) {
+               table[j] =
+                   bridge->driver->mask_memory(bridge, mem->memory[i],
+                                               mem->type);
+       }
+
+       bridge->driver->tlb_flush(mem);
+       return 0;
+}
+
+static int sgi_tioca_remove_memory(struct agp_memory *mem, off_t pg_start,
+                                  int type)
+{
+       size_t i;
+       struct agp_bridge_data *bridge;
+       u64 *table;
+
+       bridge = mem->bridge;
+       if (!bridge)
+               return -EINVAL;
+
+       if (type != 0 || mem->type != 0) {
+               return -EINVAL;
+       }
+
+       table = (u64 *)bridge->gatt_table;
+
+       for (i = pg_start; i < (mem->page_count + pg_start); i++) {
+               table[i] = 0;
+       }
+
+       bridge->driver->tlb_flush(mem);
+       return 0;
+}
+
+static void sgi_tioca_cache_flush(void)
+{
+}
+
+/*
+ * Cleanup.  Nothing to do as the CA driver owns the GART.
+ */
+
+static void sgi_tioca_cleanup(void)
+{
+}
+
+static struct agp_bridge_data *sgi_tioca_find_bridge(struct pci_dev *pdev)
+{
+       struct agp_bridge_data *bridge;
+
+       list_for_each_entry(bridge, &agp_bridges, list) {
+               if (bridge->dev->bus == pdev->bus)
+                       break;
+       }
+       return bridge;
+}
+
+struct agp_bridge_driver sgi_tioca_driver = {
+       .owner = THIS_MODULE,
+       .size_type = U16_APER_SIZE,
+       .configure = sgi_tioca_configure,
+       .fetch_size = sgi_tioca_fetch_size,
+       .cleanup = sgi_tioca_cleanup,
+       .tlb_flush = sgi_tioca_tlbflush,
+       .mask_memory = sgi_tioca_mask_memory,
+       .agp_enable = sgi_tioca_agp_enable,
+       .cache_flush = sgi_tioca_cache_flush,
+       .create_gatt_table = sgi_tioca_create_gatt_table,
+       .free_gatt_table = sgi_tioca_free_gatt_table,
+       .insert_memory = sgi_tioca_insert_memory,
+       .remove_memory = sgi_tioca_remove_memory,
+       .alloc_by_type = agp_generic_alloc_by_type,
+       .free_by_type = agp_generic_free_by_type,
+       .agp_alloc_page = sgi_tioca_alloc_page,
+       .agp_destroy_page = agp_generic_destroy_page,
+       .cant_use_aperture = 1,
+       .needs_scratch_page = 0,
+       .num_aperture_sizes = 1,
+};
+
+static int __devinit agp_sgi_init(void)
+{
+       unsigned int j;
+       struct tioca_kernel *info;
+       struct pci_dev *pdev = NULL;
+
+       if (tioca_gart_found)
+               printk(KERN_INFO PFX "SGI TIO CA GART driver initialized.\n");
+       else
+               return 0;
+
+       sgi_tioca_agp_bridges =
+           (struct agp_bridge_data **)kmalloc(tioca_gart_found *
+                                              sizeof(struct agp_bridge_data *),
+                                              GFP_KERNEL);
+
+       j = 0;
+       list_for_each_entry(info, &tioca_list, ca_list) {
+               struct list_head *tmp;
+               list_for_each(tmp, info->ca_devices) {
+                       u8 cap_ptr;
+                       pdev = pci_dev_b(tmp);
+                       if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8))
+                               continue;
+                       cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+                       if (!cap_ptr)
+                               continue;
+               }
+               sgi_tioca_agp_bridges[j] = agp_alloc_bridge();
+               printk(KERN_INFO PFX "bridge %d = 0x%p\n", j,
+                      sgi_tioca_agp_bridges[j]);
+               if (sgi_tioca_agp_bridges[j]) {
+                       sgi_tioca_agp_bridges[j]->dev = pdev;
+                       sgi_tioca_agp_bridges[j]->dev_private_data = info;
+                       sgi_tioca_agp_bridges[j]->driver = &sgi_tioca_driver;
+                       sgi_tioca_agp_bridges[j]->gart_bus_addr =
+                           info->ca_gfxap_base;
+                       sgi_tioca_agp_bridges[j]->mode = (0x7D << 24) | /* 126 requests */
+                           (0x1 << 9) |        /* SBA supported */
+                           (0x1 << 5) |        /* 64-bit addresses supported */
+                           (0x1 << 4) |        /* FW supported */
+                           (0x1 << 3) |        /* AGP 3.0 mode */
+                           0x2;        /* 8x transfer only */
+                       sgi_tioca_agp_bridges[j]->current_size =
+                           sgi_tioca_agp_bridges[j]->previous_size =
+                           (void *)&sgi_tioca_sizes[0];
+                       agp_add_bridge(sgi_tioca_agp_bridges[j]);
+               }
+               j++;
+       }
+
+       agp_find_bridge = &sgi_tioca_find_bridge;
+       return 0;
+}
+
+static void __devexit agp_sgi_cleanup(void)
+{
+       if(sgi_tioca_agp_bridges)
+               kfree(sgi_tioca_agp_bridges);
+       sgi_tioca_agp_bridges=NULL;
+}
+
+module_init(agp_sgi_init);
+module_exit(agp_sgi_cleanup);
+
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/char/mbcs.c b/drivers/char/mbcs.c
new file mode 100644 (file)
index 0000000..ac9cfa9
--- /dev/null
@@ -0,0 +1,849 @@
+/*
+ * 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 Silicon Graphics, Inc.  All rights reserved.
+ */
+
+/*
+ *     MOATB Core Services driver.
+ */
+
+#include <linux/config.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mm.h>
+#include <linux/uio.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/intr.h>
+#include <asm/sn/tiocx.h>
+#include "mbcs.h"
+
+#define MBCS_DEBUG 0
+#if MBCS_DEBUG
+#define DBG(fmt...)    printk(KERN_ALERT fmt)
+#else
+#define DBG(fmt...)
+#endif
+int mbcs_major;
+
+LIST_HEAD(soft_list);
+
+/*
+ * file operations
+ */
+struct file_operations mbcs_ops = {
+       .open = mbcs_open,
+       .llseek = mbcs_sram_llseek,
+       .read = mbcs_sram_read,
+       .write = mbcs_sram_write,
+       .mmap = mbcs_gscr_mmap,
+};
+
+struct mbcs_callback_arg {
+       int minor;
+       struct cx_dev *cx_dev;
+};
+
+static inline void mbcs_getdma_init(struct getdma *gdma)
+{
+       memset(gdma, 0, sizeof(struct getdma));
+       gdma->DoneIntEnable = 1;
+}
+
+static inline void mbcs_putdma_init(struct putdma *pdma)
+{
+       memset(pdma, 0, sizeof(struct putdma));
+       pdma->DoneIntEnable = 1;
+}
+
+static inline void mbcs_algo_init(struct algoblock *algo_soft)
+{
+       memset(algo_soft, 0, sizeof(struct algoblock));
+}
+
+static inline void mbcs_getdma_set(void *mmr,
+                      uint64_t hostAddr,
+                      uint64_t localAddr,
+                      uint64_t localRamSel,
+                      uint64_t numPkts,
+                      uint64_t amoEnable,
+                      uint64_t intrEnable,
+                      uint64_t peerIO,
+                      uint64_t amoHostDest,
+                      uint64_t amoModType, uint64_t intrHostDest,
+                      uint64_t intrVector)
+{
+       union dma_control rdma_control;
+       union dma_amo_dest amo_dest;
+       union intr_dest intr_dest;
+       union dma_localaddr local_addr;
+       union dma_hostaddr host_addr;
+
+       rdma_control.dma_control_reg = 0;
+       amo_dest.dma_amo_dest_reg = 0;
+       intr_dest.intr_dest_reg = 0;
+       local_addr.dma_localaddr_reg = 0;
+       host_addr.dma_hostaddr_reg = 0;
+
+       host_addr.dma_sys_addr = hostAddr;
+       MBCS_MMR_SET(mmr, MBCS_RD_DMA_SYS_ADDR, host_addr.dma_hostaddr_reg);
+
+       local_addr.dma_ram_addr = localAddr;
+       local_addr.dma_ram_sel = localRamSel;
+       MBCS_MMR_SET(mmr, MBCS_RD_DMA_LOC_ADDR, local_addr.dma_localaddr_reg);
+
+       rdma_control.dma_op_length = numPkts;
+       rdma_control.done_amo_en = amoEnable;
+       rdma_control.done_int_en = intrEnable;
+       rdma_control.pio_mem_n = peerIO;
+       MBCS_MMR_SET(mmr, MBCS_RD_DMA_CTRL, rdma_control.dma_control_reg);
+
+       amo_dest.dma_amo_sys_addr = amoHostDest;
+       amo_dest.dma_amo_mod_type = amoModType;
+       MBCS_MMR_SET(mmr, MBCS_RD_DMA_AMO_DEST, amo_dest.dma_amo_dest_reg);
+
+       intr_dest.address = intrHostDest;
+       intr_dest.int_vector = intrVector;
+       MBCS_MMR_SET(mmr, MBCS_RD_DMA_INT_DEST, intr_dest.intr_dest_reg);
+
+}
+
+static inline void mbcs_putdma_set(void *mmr,
+                      uint64_t hostAddr,
+                      uint64_t localAddr,
+                      uint64_t localRamSel,
+                      uint64_t numPkts,
+                      uint64_t amoEnable,
+                      uint64_t intrEnable,
+                      uint64_t peerIO,
+                      uint64_t amoHostDest,
+                      uint64_t amoModType,
+                      uint64_t intrHostDest, uint64_t intrVector)
+{
+       union dma_control wdma_control;
+       union dma_amo_dest amo_dest;
+       union intr_dest intr_dest;
+       union dma_localaddr local_addr;
+       union dma_hostaddr host_addr;
+
+       wdma_control.dma_control_reg = 0;
+       amo_dest.dma_amo_dest_reg = 0;
+       intr_dest.intr_dest_reg = 0;
+       local_addr.dma_localaddr_reg = 0;
+       host_addr.dma_hostaddr_reg = 0;
+
+       host_addr.dma_sys_addr = hostAddr;
+       MBCS_MMR_SET(mmr, MBCS_WR_DMA_SYS_ADDR, host_addr.dma_hostaddr_reg);
+
+       local_addr.dma_ram_addr = localAddr;
+       local_addr.dma_ram_sel = localRamSel;
+       MBCS_MMR_SET(mmr, MBCS_WR_DMA_LOC_ADDR, local_addr.dma_localaddr_reg);
+
+       wdma_control.dma_op_length = numPkts;
+       wdma_control.done_amo_en = amoEnable;
+       wdma_control.done_int_en = intrEnable;
+       wdma_control.pio_mem_n = peerIO;
+       MBCS_MMR_SET(mmr, MBCS_WR_DMA_CTRL, wdma_control.dma_control_reg);
+
+       amo_dest.dma_amo_sys_addr = amoHostDest;
+       amo_dest.dma_amo_mod_type = amoModType;
+       MBCS_MMR_SET(mmr, MBCS_WR_DMA_AMO_DEST, amo_dest.dma_amo_dest_reg);
+
+       intr_dest.address = intrHostDest;
+       intr_dest.int_vector = intrVector;
+       MBCS_MMR_SET(mmr, MBCS_WR_DMA_INT_DEST, intr_dest.intr_dest_reg);
+
+}
+
+static inline void mbcs_algo_set(void *mmr,
+                    uint64_t amoHostDest,
+                    uint64_t amoModType,
+                    uint64_t intrHostDest,
+                    uint64_t intrVector, uint64_t algoStepCount)
+{
+       union dma_amo_dest amo_dest;
+       union intr_dest intr_dest;
+       union algo_step step;
+
+       step.algo_step_reg = 0;
+       intr_dest.intr_dest_reg = 0;
+       amo_dest.dma_amo_dest_reg = 0;
+
+       amo_dest.dma_amo_sys_addr = amoHostDest;
+       amo_dest.dma_amo_mod_type = amoModType;
+       MBCS_MMR_SET(mmr, MBCS_ALG_AMO_DEST, amo_dest.dma_amo_dest_reg);
+
+       intr_dest.address = intrHostDest;
+       intr_dest.int_vector = intrVector;
+       MBCS_MMR_SET(mmr, MBCS_ALG_INT_DEST, intr_dest.intr_dest_reg);
+
+       step.alg_step_cnt = algoStepCount;
+       MBCS_MMR_SET(mmr, MBCS_ALG_STEP, step.algo_step_reg);
+}
+
+static inline int mbcs_getdma_start(struct mbcs_soft *soft)
+{
+       void *mmr_base;
+       struct getdma *gdma;
+       uint64_t numPkts;
+       union cm_control cm_control;
+
+       mmr_base = soft->mmr_base;
+       gdma = &soft->getdma;
+
+       /* check that host address got setup */
+       if (!gdma->hostAddr)
+               return -1;
+
+       numPkts =
+           (gdma->bytes + (MBCS_CACHELINE_SIZE - 1)) / MBCS_CACHELINE_SIZE;
+
+       /* program engine */
+       mbcs_getdma_set(mmr_base, tiocx_dma_addr(gdma->hostAddr),
+                  gdma->localAddr,
+                  (gdma->localAddr < MB2) ? 0 :
+                  (gdma->localAddr < MB4) ? 1 :
+                  (gdma->localAddr < MB6) ? 2 : 3,
+                  numPkts,
+                  gdma->DoneAmoEnable,
+                  gdma->DoneIntEnable,
+                  gdma->peerIO,
+                  gdma->amoHostDest,
+                  gdma->amoModType,
+                  gdma->intrHostDest, gdma->intrVector);
+
+       /* start engine */
+       cm_control.cm_control_reg = MBCS_MMR_GET(mmr_base, MBCS_CM_CONTROL);
+       cm_control.rd_dma_go = 1;
+       MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL, cm_control.cm_control_reg);
+
+       return 0;
+
+}
+
+static inline int mbcs_putdma_start(struct mbcs_soft *soft)
+{
+       void *mmr_base;
+       struct putdma *pdma;
+       uint64_t numPkts;
+       union cm_control cm_control;
+
+       mmr_base = soft->mmr_base;
+       pdma = &soft->putdma;
+
+       /* check that host address got setup */
+       if (!pdma->hostAddr)
+               return -1;
+
+       numPkts =
+           (pdma->bytes + (MBCS_CACHELINE_SIZE - 1)) / MBCS_CACHELINE_SIZE;
+
+       /* program engine */
+       mbcs_putdma_set(mmr_base, tiocx_dma_addr(pdma->hostAddr),
+                  pdma->localAddr,
+                  (pdma->localAddr < MB2) ? 0 :
+                  (pdma->localAddr < MB4) ? 1 :
+                  (pdma->localAddr < MB6) ? 2 : 3,
+                  numPkts,
+                  pdma->DoneAmoEnable,
+                  pdma->DoneIntEnable,
+                  pdma->peerIO,
+                  pdma->amoHostDest,
+                  pdma->amoModType,
+                  pdma->intrHostDest, pdma->intrVector);
+
+       /* start engine */
+       cm_control.cm_control_reg = MBCS_MMR_GET(mmr_base, MBCS_CM_CONTROL);
+       cm_control.wr_dma_go = 1;
+       MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL, cm_control.cm_control_reg);
+
+       return 0;
+
+}
+
+static inline int mbcs_algo_start(struct mbcs_soft *soft)
+{
+       struct algoblock *algo_soft = &soft->algo;
+       void *mmr_base = soft->mmr_base;
+       union cm_control cm_control;
+
+       if (down_interruptible(&soft->algolock))
+               return -ERESTARTSYS;
+
+       atomic_set(&soft->algo_done, 0);
+
+       mbcs_algo_set(mmr_base,
+                algo_soft->amoHostDest,
+                algo_soft->amoModType,
+                algo_soft->intrHostDest,
+                algo_soft->intrVector, algo_soft->algoStepCount);
+
+       /* start algorithm */
+       cm_control.cm_control_reg = MBCS_MMR_GET(mmr_base, MBCS_CM_CONTROL);
+       cm_control.alg_done_int_en = 1;
+       cm_control.alg_go = 1;
+       MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL, cm_control.cm_control_reg);
+
+       up(&soft->algolock);
+
+       return 0;
+}
+
+static inline ssize_t
+do_mbcs_sram_dmawrite(struct mbcs_soft *soft, uint64_t hostAddr,
+                     size_t len, loff_t * off)
+{
+       int rv = 0;
+
+       if (down_interruptible(&soft->dmawritelock))
+               return -ERESTARTSYS;
+
+       atomic_set(&soft->dmawrite_done, 0);
+
+       soft->putdma.hostAddr = hostAddr;
+       soft->putdma.localAddr = *off;
+       soft->putdma.bytes = len;
+
+       if (mbcs_putdma_start(soft) < 0) {
+               DBG(KERN_ALERT "do_mbcs_sram_dmawrite: "
+                                       "mbcs_putdma_start failed\n");
+               rv = -EAGAIN;
+               goto dmawrite_exit;
+       }
+
+       if (wait_event_interruptible(soft->dmawrite_queue,
+                                       atomic_read(&soft->dmawrite_done))) {
+               rv = -ERESTARTSYS;
+               goto dmawrite_exit;
+       }
+
+       rv = len;
+       *off += len;
+
+dmawrite_exit:
+       up(&soft->dmawritelock);
+
+       return rv;
+}
+
+static inline ssize_t
+do_mbcs_sram_dmaread(struct mbcs_soft *soft, uint64_t hostAddr,
+                    size_t len, loff_t * off)
+{
+       int rv = 0;
+
+       if (down_interruptible(&soft->dmareadlock))
+               return -ERESTARTSYS;
+
+       atomic_set(&soft->dmawrite_done, 0);
+
+       soft->getdma.hostAddr = hostAddr;
+       soft->getdma.localAddr = *off;
+       soft->getdma.bytes = len;
+
+       if (mbcs_getdma_start(soft) < 0) {
+               DBG(KERN_ALERT "mbcs_strategy: mbcs_getdma_start failed\n");
+               rv = -EAGAIN;
+               goto dmaread_exit;
+       }
+
+       if (wait_event_interruptible(soft->dmaread_queue,
+                                       atomic_read(&soft->dmaread_done))) {
+               rv = -ERESTARTSYS;
+               goto dmaread_exit;
+       }
+
+       rv = len;
+       *off += len;
+
+dmaread_exit:
+       up(&soft->dmareadlock);
+
+       return rv;
+}
+
+int mbcs_open(struct inode *ip, struct file *fp)
+{
+       struct mbcs_soft *soft;
+       int minor;
+
+       minor = iminor(ip);
+
+       list_for_each_entry(soft, &soft_list, list) {
+               if (soft->nasid == minor) {
+                       fp->private_data = soft->cxdev;
+                       return 0;
+               }
+       }
+
+       return -ENODEV;
+}
+
+ssize_t mbcs_sram_read(struct file * fp, char __user *buf, size_t len, loff_t * off)
+{
+       struct cx_dev *cx_dev = fp->private_data;
+       struct mbcs_soft *soft = cx_dev->soft;
+       uint64_t hostAddr;
+       int rv = 0;
+
+       hostAddr = __get_dma_pages(GFP_KERNEL, get_order(len));
+       if (hostAddr == 0)
+               return -ENOMEM;
+
+       rv = do_mbcs_sram_dmawrite(soft, hostAddr, len, off);
+       if (rv < 0)
+               goto exit;
+
+       if (copy_to_user(buf, (void *)hostAddr, len))
+               rv = -EFAULT;
+
+      exit:
+       free_pages(hostAddr, get_order(len));
+
+       return rv;
+}
+
+ssize_t
+mbcs_sram_write(struct file * fp, const char __user *buf, size_t len, loff_t * off)
+{
+       struct cx_dev *cx_dev = fp->private_data;
+       struct mbcs_soft *soft = cx_dev->soft;
+       uint64_t hostAddr;
+       int rv = 0;
+
+       hostAddr = __get_dma_pages(GFP_KERNEL, get_order(len));
+       if (hostAddr == 0)
+               return -ENOMEM;
+
+       if (copy_from_user((void *)hostAddr, buf, len)) {
+               rv = -EFAULT;
+               goto exit;
+       }
+
+       rv = do_mbcs_sram_dmaread(soft, hostAddr, len, off);
+
+      exit:
+       free_pages(hostAddr, get_order(len));
+
+       return rv;
+}
+
+loff_t mbcs_sram_llseek(struct file * filp, loff_t off, int whence)
+{
+       loff_t newpos;
+
+       switch (whence) {
+       case 0:         /* SEEK_SET */
+               newpos = off;
+               break;
+
+       case 1:         /* SEEK_CUR */
+               newpos = filp->f_pos + off;
+               break;
+
+       case 2:         /* SEEK_END */
+               newpos = MBCS_SRAM_SIZE + off;
+               break;
+
+       default:                /* can't happen */
+               return -EINVAL;
+       }
+
+       if (newpos < 0)
+               return -EINVAL;
+
+       filp->f_pos = newpos;
+
+       return newpos;
+}
+
+static uint64_t mbcs_pioaddr(struct mbcs_soft *soft, uint64_t offset)
+{
+       uint64_t mmr_base;
+
+       mmr_base = (uint64_t) (soft->mmr_base + offset);
+
+       return mmr_base;
+}
+
+static void mbcs_debug_pioaddr_set(struct mbcs_soft *soft)
+{
+       soft->debug_addr = mbcs_pioaddr(soft, MBCS_DEBUG_START);
+}
+
+static void mbcs_gscr_pioaddr_set(struct mbcs_soft *soft)
+{
+       soft->gscr_addr = mbcs_pioaddr(soft, MBCS_GSCR_START);
+}
+
+int mbcs_gscr_mmap(struct file *fp, struct vm_area_struct *vma)
+{
+       struct cx_dev *cx_dev = fp->private_data;
+       struct mbcs_soft *soft = cx_dev->soft;
+
+       if (vma->vm_pgoff != 0)
+               return -EINVAL;
+
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+       /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
+       if (remap_pfn_range(vma,
+                           vma->vm_start,
+                           __pa(soft->gscr_addr) >> PAGE_SHIFT,
+                           PAGE_SIZE,
+                           vma->vm_page_prot))
+               return -EAGAIN;
+
+       return 0;
+}
+
+/**
+ * mbcs_completion_intr_handler - Primary completion handler.
+ * @irq: irq
+ * @arg: soft struct for device
+ * @ep: regs
+ *
+ */
+static irqreturn_t
+mbcs_completion_intr_handler(int irq, void *arg, struct pt_regs *ep)
+{
+       struct mbcs_soft *soft = (struct mbcs_soft *)arg;
+       void *mmr_base;
+       union cm_status cm_status;
+       union cm_control cm_control;
+
+       mmr_base = soft->mmr_base;
+       cm_status.cm_status_reg = MBCS_MMR_GET(mmr_base, MBCS_CM_STATUS);
+
+       if (cm_status.rd_dma_done) {
+               /* stop dma-read engine, clear status */
+               cm_control.cm_control_reg =
+                   MBCS_MMR_GET(mmr_base, MBCS_CM_CONTROL);
+               cm_control.rd_dma_clr = 1;
+               MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL,
+                            cm_control.cm_control_reg);
+               atomic_set(&soft->dmaread_done, 1);
+               wake_up(&soft->dmaread_queue);
+       }
+       if (cm_status.wr_dma_done) {
+               /* stop dma-write engine, clear status */
+               cm_control.cm_control_reg =
+                   MBCS_MMR_GET(mmr_base, MBCS_CM_CONTROL);
+               cm_control.wr_dma_clr = 1;
+               MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL,
+                            cm_control.cm_control_reg);
+               atomic_set(&soft->dmawrite_done, 1);
+               wake_up(&soft->dmawrite_queue);
+       }
+       if (cm_status.alg_done) {
+               /* clear status */
+               cm_control.cm_control_reg =
+                   MBCS_MMR_GET(mmr_base, MBCS_CM_CONTROL);
+               cm_control.alg_done_clr = 1;
+               MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL,
+                            cm_control.cm_control_reg);
+               atomic_set(&soft->algo_done, 1);
+               wake_up(&soft->algo_queue);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * mbcs_intr_alloc - Allocate interrupts.
+ * @dev: device pointer
+ *
+ */
+static int mbcs_intr_alloc(struct cx_dev *dev)
+{
+       struct sn_irq_info *sn_irq;
+       struct mbcs_soft *soft;
+       struct getdma *getdma;
+       struct putdma *putdma;
+       struct algoblock *algo;
+
+       soft = dev->soft;
+       getdma = &soft->getdma;
+       putdma = &soft->putdma;
+       algo = &soft->algo;
+
+       soft->get_sn_irq = NULL;
+       soft->put_sn_irq = NULL;
+       soft->algo_sn_irq = NULL;
+
+       sn_irq = tiocx_irq_alloc(dev->cx_id.nasid, TIOCX_CORELET, -1, -1, -1);
+       if (sn_irq == NULL)
+               return -EAGAIN;
+       soft->get_sn_irq = sn_irq;
+       getdma->intrHostDest = sn_irq->irq_xtalkaddr;
+       getdma->intrVector = sn_irq->irq_irq;
+       if (request_irq(sn_irq->irq_irq,
+                       (void *)mbcs_completion_intr_handler, SA_SHIRQ,
+                       "MBCS get intr", (void *)soft)) {
+               tiocx_irq_free(soft->get_sn_irq);
+               return -EAGAIN;
+       }
+
+       sn_irq = tiocx_irq_alloc(dev->cx_id.nasid, TIOCX_CORELET, -1, -1, -1);
+       if (sn_irq == NULL) {
+               free_irq(soft->get_sn_irq->irq_irq, soft);
+               tiocx_irq_free(soft->get_sn_irq);
+               return -EAGAIN;
+       }
+       soft->put_sn_irq = sn_irq;
+       putdma->intrHostDest = sn_irq->irq_xtalkaddr;
+       putdma->intrVector = sn_irq->irq_irq;
+       if (request_irq(sn_irq->irq_irq,
+                       (void *)mbcs_completion_intr_handler, SA_SHIRQ,
+                       "MBCS put intr", (void *)soft)) {
+               tiocx_irq_free(soft->put_sn_irq);
+               free_irq(soft->get_sn_irq->irq_irq, soft);
+               tiocx_irq_free(soft->get_sn_irq);
+               return -EAGAIN;
+       }
+
+       sn_irq = tiocx_irq_alloc(dev->cx_id.nasid, TIOCX_CORELET, -1, -1, -1);
+       if (sn_irq == NULL) {
+               free_irq(soft->put_sn_irq->irq_irq, soft);
+               tiocx_irq_free(soft->put_sn_irq);
+               free_irq(soft->get_sn_irq->irq_irq, soft);
+               tiocx_irq_free(soft->get_sn_irq);
+               return -EAGAIN;
+       }
+       soft->algo_sn_irq = sn_irq;
+       algo->intrHostDest = sn_irq->irq_xtalkaddr;
+       algo->intrVector = sn_irq->irq_irq;
+       if (request_irq(sn_irq->irq_irq,
+                       (void *)mbcs_completion_intr_handler, SA_SHIRQ,
+                       "MBCS algo intr", (void *)soft)) {
+               tiocx_irq_free(soft->algo_sn_irq);
+               free_irq(soft->put_sn_irq->irq_irq, soft);
+               tiocx_irq_free(soft->put_sn_irq);
+               free_irq(soft->get_sn_irq->irq_irq, soft);
+               tiocx_irq_free(soft->get_sn_irq);
+               return -EAGAIN;
+       }
+
+       return 0;
+}
+
+/**
+ * mbcs_intr_dealloc - Remove interrupts.
+ * @dev: device pointer
+ *
+ */
+static void mbcs_intr_dealloc(struct cx_dev *dev)
+{
+       struct mbcs_soft *soft;
+
+       soft = dev->soft;
+
+       free_irq(soft->get_sn_irq->irq_irq, soft);
+       tiocx_irq_free(soft->get_sn_irq);
+       free_irq(soft->put_sn_irq->irq_irq, soft);
+       tiocx_irq_free(soft->put_sn_irq);
+       free_irq(soft->algo_sn_irq->irq_irq, soft);
+       tiocx_irq_free(soft->algo_sn_irq);
+}
+
+static inline int mbcs_hw_init(struct mbcs_soft *soft)
+{
+       void *mmr_base = soft->mmr_base;
+       union cm_control cm_control;
+       union cm_req_timeout cm_req_timeout;
+       uint64_t err_stat;
+
+       cm_req_timeout.cm_req_timeout_reg =
+           MBCS_MMR_GET(mmr_base, MBCS_CM_REQ_TOUT);
+
+       cm_req_timeout.time_out = MBCS_CM_CONTROL_REQ_TOUT_MASK;
+       MBCS_MMR_SET(mmr_base, MBCS_CM_REQ_TOUT,
+                    cm_req_timeout.cm_req_timeout_reg);
+
+       mbcs_gscr_pioaddr_set(soft);
+       mbcs_debug_pioaddr_set(soft);
+
+       /* clear errors */
+       err_stat = MBCS_MMR_GET(mmr_base, MBCS_CM_ERR_STAT);
+       MBCS_MMR_SET(mmr_base, MBCS_CM_CLR_ERR_STAT, err_stat);
+       MBCS_MMR_ZERO(mmr_base, MBCS_CM_ERROR_DETAIL1);
+
+       /* enable interrupts */
+       /* turn off 2^23 (INT_EN_PIO_REQ_ADDR_INV) */
+       MBCS_MMR_SET(mmr_base, MBCS_CM_ERR_INT_EN, 0x3ffffff7e00ffUL);
+
+       /* arm status regs and clear engines */
+       cm_control.cm_control_reg = MBCS_MMR_GET(mmr_base, MBCS_CM_CONTROL);
+       cm_control.rearm_stat_regs = 1;
+       cm_control.alg_clr = 1;
+       cm_control.wr_dma_clr = 1;
+       cm_control.rd_dma_clr = 1;
+
+       MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL, cm_control.cm_control_reg);
+
+       return 0;
+}
+
+static ssize_t show_algo(struct device *dev, char *buf)
+{
+       struct cx_dev *cx_dev = to_cx_dev(dev);
+       struct mbcs_soft *soft = cx_dev->soft;
+       uint64_t debug0;
+
+       /*
+        * By convention, the first debug register contains the
+        * algorithm number and revision.
+        */
+       debug0 = *(uint64_t *) soft->debug_addr;
+
+       return sprintf(buf, "0x%lx 0x%lx\n",
+                      (debug0 >> 32), (debug0 & 0xffffffff));
+}
+
+static ssize_t store_algo(struct device *dev, const char *buf, size_t count)
+{
+       int n;
+       struct cx_dev *cx_dev = to_cx_dev(dev);
+       struct mbcs_soft *soft = cx_dev->soft;
+
+       if (count <= 0)
+               return 0;
+
+       n = simple_strtoul(buf, NULL, 0);
+
+       if (n == 1) {
+               mbcs_algo_start(soft);
+               if (wait_event_interruptible(soft->algo_queue,
+                                       atomic_read(&soft->algo_done)))
+                       return -ERESTARTSYS;
+       }
+
+       return count;
+}
+
+DEVICE_ATTR(algo, 0644, show_algo, store_algo);
+
+/**
+ * mbcs_probe - Initialize for device
+ * @dev: device pointer
+ * @device_id: id table pointer
+ *
+ */
+static int mbcs_probe(struct cx_dev *dev, const struct cx_device_id *id)
+{
+       struct mbcs_soft *soft;
+
+       dev->soft = NULL;
+
+       soft = kcalloc(1, sizeof(struct mbcs_soft), GFP_KERNEL);
+       if (soft == NULL)
+               return -ENOMEM;
+
+       soft->nasid = dev->cx_id.nasid;
+       list_add(&soft->list, &soft_list);
+       soft->mmr_base = (void *)tiocx_swin_base(dev->cx_id.nasid);
+       dev->soft = soft;
+       soft->cxdev = dev;
+
+       init_waitqueue_head(&soft->dmawrite_queue);
+       init_waitqueue_head(&soft->dmaread_queue);
+       init_waitqueue_head(&soft->algo_queue);
+
+       init_MUTEX(&soft->dmawritelock);
+       init_MUTEX(&soft->dmareadlock);
+       init_MUTEX(&soft->algolock);
+
+       mbcs_getdma_init(&soft->getdma);
+       mbcs_putdma_init(&soft->putdma);
+       mbcs_algo_init(&soft->algo);
+
+       mbcs_hw_init(soft);
+
+       /* Allocate interrupts */
+       mbcs_intr_alloc(dev);
+
+       device_create_file(&dev->dev, &dev_attr_algo);
+
+       return 0;
+}
+
+static int mbcs_remove(struct cx_dev *dev)
+{
+       if (dev->soft) {
+               mbcs_intr_dealloc(dev);
+               kfree(dev->soft);
+       }
+
+       device_remove_file(&dev->dev, &dev_attr_algo);
+
+       return 0;
+}
+
+const struct cx_device_id __devinitdata mbcs_id_table[] = {
+       {
+        .part_num = MBCS_PART_NUM,
+        .mfg_num = MBCS_MFG_NUM,
+        },
+       {
+        .part_num = MBCS_PART_NUM_ALG0,
+        .mfg_num = MBCS_MFG_NUM,
+        },
+       {0, 0}
+};
+
+MODULE_DEVICE_TABLE(cx, mbcs_id_table);
+
+struct cx_drv mbcs_driver = {
+       .name = DEVICE_NAME,
+       .id_table = mbcs_id_table,
+       .probe = mbcs_probe,
+       .remove = mbcs_remove,
+};
+
+static void __exit mbcs_exit(void)
+{
+       int rv;
+
+       rv = unregister_chrdev(mbcs_major, DEVICE_NAME);
+       if (rv < 0)
+               DBG(KERN_ALERT "Error in unregister_chrdev: %d\n", rv);
+
+       cx_driver_unregister(&mbcs_driver);
+}
+
+static int __init mbcs_init(void)
+{
+       int rv;
+
+       // Put driver into chrdevs[].  Get major number.
+       rv = register_chrdev(mbcs_major, DEVICE_NAME, &mbcs_ops);
+       if (rv < 0) {
+               DBG(KERN_ALERT "mbcs_init: can't get major number. %d\n", rv);
+               return rv;
+       }
+       mbcs_major = rv;
+
+       return cx_driver_register(&mbcs_driver);
+}
+
+module_init(mbcs_init);
+module_exit(mbcs_exit);
+
+MODULE_AUTHOR("Bruce Losure <blosure@sgi.com>");
+MODULE_DESCRIPTION("Driver for MOATB Core Services");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/snsc_event.c b/drivers/char/snsc_event.c
new file mode 100644 (file)
index 0000000..d692af5
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * SN Platform system controller communication support
+ *
+ * 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.
+ */
+
+/*
+ * System controller event handler
+ *
+ * These routines deal with environmental events arriving from the
+ * system controllers.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/byteorder/generic.h>
+#include <asm/sn/sn_sal.h>
+#include "snsc.h"
+
+static struct subch_data_s *event_sd;
+
+void scdrv_event(unsigned long);
+DECLARE_TASKLET(sn_sysctl_event, scdrv_event, 0);
+
+/*
+ * scdrv_event_interrupt
+ *
+ * Pull incoming environmental events off the physical link to the
+ * system controller and put them in a temporary holding area in SAL.
+ * Schedule scdrv_event() to move them along to their ultimate
+ * destination.
+ */
+static irqreturn_t
+scdrv_event_interrupt(int irq, void *subch_data, struct pt_regs *regs)
+{
+       struct subch_data_s *sd = subch_data;
+       unsigned long flags;
+       int status;
+
+       spin_lock_irqsave(&sd->sd_rlock, flags);
+       status = ia64_sn_irtr_intr(sd->sd_nasid, sd->sd_subch);
+
+       if ((status > 0) && (status & SAL_IROUTER_INTR_RECV)) {
+               tasklet_schedule(&sn_sysctl_event);
+       }
+       spin_unlock_irqrestore(&sd->sd_rlock, flags);
+       return IRQ_HANDLED;
+}
+
+
+/*
+ * scdrv_parse_event
+ *
+ * Break an event (as read from SAL) into useful pieces so we can decide
+ * what to do with it.
+ */
+static int
+scdrv_parse_event(char *event, int *src, int *code, int *esp_code, char *desc)
+{
+       char *desc_end;
+
+       /* record event source address */
+       *src = be32_to_cpup((__be32 *)event);
+       event += 4;                     /* move on to event code */
+
+       /* record the system controller's event code */
+       *code = be32_to_cpup((__be32 *)event);
+       event += 4;                     /* move on to event arguments */
+
+       /* how many arguments are in the packet? */
+       if (*event++ != 2) {
+               /* if not 2, give up */
+               return -1;
+       }
+
+       /* parse out the ESP code */
+       if (*event++ != IR_ARG_INT) {
+               /* not an integer argument, so give up */
+               return -1;
+       }
+       *esp_code = be32_to_cpup((__be32 *)event);
+       event += 4;
+
+       /* parse out the event description */
+       if (*event++ != IR_ARG_ASCII) {
+               /* not an ASCII string, so give up */
+               return -1;
+       }
+       event[CHUNKSIZE-1] = '\0';      /* ensure this string ends! */
+       event += 2;                     /* skip leading CR/LF */
+       desc_end = desc + sprintf(desc, "%s", event);
+
+       /* strip trailing CR/LF (if any) */
+       for (desc_end--;
+            (desc_end != desc) && ((*desc_end == 0xd) || (*desc_end == 0xa));
+            desc_end--) {
+               *desc_end = '\0';
+       }
+
+       return 0;
+}
+
+
+/*
+ * scdrv_event_severity
+ *
+ * Figure out how urgent a message we should write to the console/syslog
+ * via printk.
+ */
+static char *
+scdrv_event_severity(int code)
+{
+       int ev_class = (code & EV_CLASS_MASK);
+       int ev_severity = (code & EV_SEVERITY_MASK);
+       char *pk_severity = KERN_NOTICE;
+
+       switch (ev_class) {
+       case EV_CLASS_POWER:
+               switch (ev_severity) {
+               case EV_SEVERITY_POWER_LOW_WARNING:
+               case EV_SEVERITY_POWER_HIGH_WARNING:
+                       pk_severity = KERN_WARNING;
+                       break;
+               case EV_SEVERITY_POWER_HIGH_FAULT:
+               case EV_SEVERITY_POWER_LOW_FAULT:
+                       pk_severity = KERN_ALERT;
+                       break;
+               }
+               break;
+       case EV_CLASS_FAN:
+               switch (ev_severity) {
+               case EV_SEVERITY_FAN_WARNING:
+                       pk_severity = KERN_WARNING;
+                       break;
+               case EV_SEVERITY_FAN_FAULT:
+                       pk_severity = KERN_CRIT;
+                       break;
+               }
+               break;
+       case EV_CLASS_TEMP:
+               switch (ev_severity) {
+               case EV_SEVERITY_TEMP_ADVISORY:
+                       pk_severity = KERN_WARNING;
+                       break;
+               case EV_SEVERITY_TEMP_CRITICAL:
+                       pk_severity = KERN_CRIT;
+                       break;
+               case EV_SEVERITY_TEMP_FAULT:
+                       pk_severity = KERN_ALERT;
+                       break;
+               }
+               break;
+       case EV_CLASS_ENV:
+               pk_severity = KERN_ALERT;
+               break;
+       case EV_CLASS_TEST_FAULT:
+               pk_severity = KERN_ALERT;
+               break;
+       case EV_CLASS_TEST_WARNING:
+               pk_severity = KERN_WARNING;
+               break;
+       case EV_CLASS_PWRD_NOTIFY:
+               pk_severity = KERN_ALERT;
+               break;
+       }
+
+       return pk_severity;
+}
+
+
+/*
+ * scdrv_dispatch_event
+ *
+ * Do the right thing with an incoming event.  That's often nothing
+ * more than printing it to the system log.  For power-down notifications
+ * we start a graceful shutdown.
+ */
+static void
+scdrv_dispatch_event(char *event, int len)
+{
+       int code, esp_code, src;
+       char desc[CHUNKSIZE];
+       char *severity;
+
+       if (scdrv_parse_event(event, &src, &code, &esp_code, desc) < 0) {
+               /* ignore uninterpretible event */
+               return;
+       }
+
+       /* how urgent is the message? */
+       severity = scdrv_event_severity(code);
+
+       if ((code & EV_CLASS_MASK) == EV_CLASS_PWRD_NOTIFY) {
+               struct task_struct *p;
+
+               /* give a SIGPWR signal to init proc */
+
+               /* first find init's task */
+               read_lock(&tasklist_lock);
+               for_each_process(p) {
+                       if (p->pid == 1)
+                               break;
+               }
+               if (p) { /* we found init's task */
+                       printk(KERN_EMERG "Power off indication received. Initiating power fail sequence...\n");
+                       force_sig(SIGPWR, p);
+               } else { /* failed to find init's task - just give message(s) */
+                       printk(KERN_WARNING "Failed to find init proc to handle power off!\n");
+                       printk("%s|$(0x%x)%s\n", severity, esp_code, desc);
+               }
+               read_unlock(&tasklist_lock);
+       } else {
+               /* print to system log */
+               printk("%s|$(0x%x)%s\n", severity, esp_code, desc);
+       }
+}
+
+
+/*
+ * scdrv_event
+ *
+ * Called as a tasklet when an event arrives from the L1.  Read the event
+ * from where it's temporarily stored in SAL and call scdrv_dispatch_event()
+ * to send it on its way.  Keep trying to read events until SAL indicates
+ * that there are no more immediately available.
+ */
+void
+scdrv_event(unsigned long dummy)
+{
+       int status;
+       int len;
+       unsigned long flags;
+       struct subch_data_s *sd = event_sd;
+
+       /* anything to read? */
+       len = CHUNKSIZE;
+       spin_lock_irqsave(&sd->sd_rlock, flags);
+       status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch,
+                                  sd->sd_rb, &len);
+
+       while (!(status < 0)) {
+               spin_unlock_irqrestore(&sd->sd_rlock, flags);
+               scdrv_dispatch_event(sd->sd_rb, len);
+               len = CHUNKSIZE;
+               spin_lock_irqsave(&sd->sd_rlock, flags);
+               status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch,
+                                          sd->sd_rb, &len);
+       }
+       spin_unlock_irqrestore(&sd->sd_rlock, flags);
+}
+
+
+/*
+ * scdrv_event_init
+ *
+ * Sets up a system controller subchannel to begin receiving event
+ * messages. This is sort of a specialized version of scdrv_open()
+ * in drivers/char/sn_sysctl.c.
+ */
+void
+scdrv_event_init(struct sysctl_data_s *scd)
+{
+       int rv;
+
+       event_sd = kmalloc(sizeof (struct subch_data_s), GFP_KERNEL);
+       if (event_sd == NULL) {
+               printk(KERN_WARNING "%s: couldn't allocate subchannel info"
+                      " for event monitoring\n", __FUNCTION__);
+               return;
+       }
+
+       /* initialize subch_data_s fields */
+       memset(event_sd, 0, sizeof (struct subch_data_s));
+       event_sd->sd_nasid = scd->scd_nasid;
+       spin_lock_init(&event_sd->sd_rlock);
+
+       /* ask the system controllers to send events to this node */
+       event_sd->sd_subch = ia64_sn_sysctl_event_init(scd->scd_nasid);
+
+       if (event_sd->sd_subch < 0) {
+               kfree(event_sd);
+               printk(KERN_WARNING "%s: couldn't open event subchannel\n",
+                      __FUNCTION__);
+               return;
+       }
+
+       /* hook event subchannel up to the system controller interrupt */
+       rv = request_irq(SGI_UART_VECTOR, scdrv_event_interrupt,
+                        SA_SHIRQ | SA_INTERRUPT,
+                        "system controller events", event_sd);
+       if (rv) {
+               printk(KERN_WARNING "%s: irq request failed (%d)\n",
+                      __FUNCTION__, rv);
+               ia64_sn_irtr_close(event_sd->sd_nasid, event_sd->sd_subch);
+               kfree(event_sd);
+               return;
+       }
+}
+
+
diff --git a/drivers/char/tb0219.c b/drivers/char/tb0219.c
new file mode 100644 (file)
index 0000000..5413f29
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ *  Driver for TANBAC TB0219 base board.
+ *
+ *  Copyright (C) 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/device.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include <asm/reboot.h>
+
+MODULE_AUTHOR("Yoichi Yuasa <yuasa@hh.iij4u.or.jp>");
+MODULE_DESCRIPTION("TANBAC TB0219 base board driver");
+MODULE_LICENSE("GPL");
+
+static int major;      /* default is dynamic major device number */
+module_param(major, int, 0);
+MODULE_PARM_DESC(major, "Major device number");
+
+static void (*old_machine_restart)(char *command);
+static void __iomem *tb0219_base;
+static spinlock_t tb0219_lock;
+
+#define tb0219_read(offset)            readw(tb0219_base + (offset))
+#define tb0219_write(offset, value)    writew((value), tb0219_base + (offset))
+
+#define TB0219_START   0x0a000000UL
+#define TB0219_SIZE    0x20UL
+
+#define TB0219_LED                     0x00
+#define TB0219_GPIO_INPUT              0x02
+#define TB0219_GPIO_OUTPUT             0x04
+#define TB0219_DIP_SWITCH              0x06
+#define TB0219_MISC                    0x08
+#define TB0219_RESET                   0x0e
+#define TB0219_PCI_SLOT1_IRQ_STATUS    0x10
+#define TB0219_PCI_SLOT2_IRQ_STATUS    0x12
+#define TB0219_PCI_SLOT3_IRQ_STATUS    0x14
+
+typedef enum {
+       TYPE_LED,
+       TYPE_GPIO_OUTPUT,
+} tb0219_type_t;
+
+/*
+ * Minor device number
+ *      0 = 7 segment LED
+ *
+ *     16 = GPIO IN 0
+ *     17 = GPIO IN 1
+ *     18 = GPIO IN 2
+ *     19 = GPIO IN 3
+ *     20 = GPIO IN 4
+ *     21 = GPIO IN 5
+ *     22 = GPIO IN 6
+ *     23 = GPIO IN 7
+ *
+ *     32 = GPIO OUT 0
+ *     33 = GPIO OUT 1
+ *     34 = GPIO OUT 2
+ *     35 = GPIO OUT 3
+ *     36 = GPIO OUT 4
+ *     37 = GPIO OUT 5
+ *     38 = GPIO OUT 6
+ *     39 = GPIO OUT 7
+ *
+ *     48 = DIP switch 1
+ *     49 = DIP switch 2
+ *     50 = DIP switch 3
+ *     51 = DIP switch 4
+ *     52 = DIP switch 5
+ *     53 = DIP switch 6
+ *     54 = DIP switch 7
+ *     55 = DIP switch 8
+ */
+
+static inline char get_led(void)
+{
+       return (char)tb0219_read(TB0219_LED);
+}
+
+static inline char get_gpio_input_pin(unsigned int pin)
+{
+       uint16_t values;
+
+       values = tb0219_read(TB0219_GPIO_INPUT);
+       if (values & (1 << pin))
+               return '1';
+
+       return '0';
+}
+
+static inline char get_gpio_output_pin(unsigned int pin)
+{
+       uint16_t values;
+
+       values = tb0219_read(TB0219_GPIO_OUTPUT);
+       if (values & (1 << pin))
+               return '1';
+
+       return '0';
+}
+
+static inline char get_dip_switch(unsigned int pin)
+{
+       uint16_t values;
+
+       values = tb0219_read(TB0219_DIP_SWITCH);
+       if (values & (1 << pin))
+               return '1';
+
+       return '0';
+}
+
+static inline int set_led(char command)
+{
+       tb0219_write(TB0219_LED, command);
+
+       return 0;
+}
+
+static inline int set_gpio_output_pin(unsigned int pin, char command)
+{
+       unsigned long flags;
+       uint16_t value;
+
+       if (command != '0' && command != '1')
+               return -EINVAL;
+
+       spin_lock_irqsave(&tb0219_lock, flags);
+       value = tb0219_read(TB0219_GPIO_OUTPUT);
+       if (command == '0')
+               value &= ~(1 << pin);
+       else
+               value |= 1 << pin;
+       tb0219_write(TB0219_GPIO_OUTPUT, value);
+       spin_unlock_irqrestore(&tb0219_lock, flags);
+
+       return 0;
+
+}
+
+static ssize_t tanbac_tb0219_read(struct file *file, char __user *buf, size_t len,
+                                  loff_t *ppos)
+{
+       unsigned int minor;
+       char value;
+
+       minor = iminor(file->f_dentry->d_inode);
+       switch (minor) {
+       case 0:
+               value = get_led();
+               break;
+       case 16 ... 23:
+               value = get_gpio_input_pin(minor - 16);
+               break;
+       case 32 ... 39:
+               value = get_gpio_output_pin(minor - 32);
+               break;
+       case 48 ... 55:
+               value = get_dip_switch(minor - 48);
+               break;
+       default:
+               return -EBADF;
+       }
+
+       if (len <= 0)
+               return -EFAULT;
+
+       if (put_user(value, buf))
+               return -EFAULT;
+
+       return 1;
+}
+
+static ssize_t tanbac_tb0219_write(struct file *file, const char __user *data,
+                                   size_t len, loff_t *ppos)
+{
+       unsigned int minor;
+       tb0219_type_t type;
+       size_t i;
+       int retval = 0;
+       char c;
+
+       minor = iminor(file->f_dentry->d_inode);
+       switch (minor) {
+       case 0:
+               type = TYPE_LED;
+               break;
+       case 32 ... 39:
+               type = TYPE_GPIO_OUTPUT;
+               break;
+       default:
+               return -EBADF;
+       }
+
+       for (i = 0; i < len; i++) {
+               if (get_user(c, data + i))
+                       return -EFAULT;
+
+               switch (type) {
+               case TYPE_LED:
+                       retval = set_led(c);
+                       break;
+               case TYPE_GPIO_OUTPUT:
+                       retval = set_gpio_output_pin(minor - 32, c);
+                       break;
+               }
+
+               if (retval < 0)
+                       break;
+       }
+
+       return i;
+}
+
+static int tanbac_tb0219_open(struct inode *inode, struct file *file)
+{
+       unsigned int minor;
+
+       minor = iminor(inode);
+       switch (minor) {
+       case 0:
+       case 16 ... 23:
+       case 32 ... 39:
+       case 48 ... 55:
+               return nonseekable_open(inode, file);
+       default:
+               break;
+       }
+
+       return -EBADF;
+}
+
+static int tanbac_tb0219_release(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+static struct file_operations tb0219_fops = {
+       .owner          = THIS_MODULE,
+       .read           = tanbac_tb0219_read,
+       .write          = tanbac_tb0219_write,
+       .open           = tanbac_tb0219_open,
+       .release        = tanbac_tb0219_release,
+};
+
+static void tb0219_restart(char *command)
+{
+       tb0219_write(TB0219_RESET, 0);
+}
+
+static int tb0219_probe(struct device *dev)
+{
+       int retval;
+
+       if (request_mem_region(TB0219_START, TB0219_SIZE, "TB0219") == NULL)
+               return -EBUSY;
+
+       tb0219_base = ioremap(TB0219_START, TB0219_SIZE);
+       if (tb0219_base == NULL) {
+               release_mem_region(TB0219_START, TB0219_SIZE);
+               return -ENOMEM;
+       }
+
+       retval = register_chrdev(major, "TB0219", &tb0219_fops);
+       if (retval < 0) {
+               iounmap(tb0219_base);
+               tb0219_base = NULL;
+               release_mem_region(TB0219_START, TB0219_SIZE);
+               return retval;
+       }
+
+       spin_lock_init(&tb0219_lock);
+
+       old_machine_restart = _machine_restart;
+       _machine_restart = tb0219_restart;
+
+       if (major == 0) {
+               major = retval;
+               printk(KERN_INFO "TB0219: major number %d\n", major);
+       }
+
+       return 0;
+}
+
+static int tb0219_remove(struct device *dev)
+{
+       _machine_restart = old_machine_restart;
+
+       iounmap(tb0219_base);
+       tb0219_base = NULL;
+
+       release_mem_region(TB0219_START, TB0219_SIZE);
+
+       return 0;
+}
+
+static struct platform_device *tb0219_platform_device;
+
+static struct device_driver tb0219_device_driver = {
+       .name           = "TB0219",
+       .bus            = &platform_bus_type,
+       .probe          = tb0219_probe,
+       .remove         = tb0219_remove,
+};
+
+static int __devinit tanbac_tb0219_init(void)
+{
+       int retval;
+
+       tb0219_platform_device = platform_device_register_simple("TB0219", -1, NULL, 0);
+       if (IS_ERR(tb0219_platform_device))
+               return PTR_ERR(tb0219_platform_device);
+
+       retval = driver_register(&tb0219_device_driver);
+       if (retval < 0)
+               platform_device_unregister(tb0219_platform_device);
+
+       return retval;
+}
+
+static void __devexit tanbac_tb0219_exit(void)
+{
+       driver_unregister(&tb0219_device_driver);
+
+       platform_device_unregister(tb0219_platform_device);
+}
+
+module_init(tanbac_tb0219_init);
+module_exit(tanbac_tb0219_exit);
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
new file mode 100644 (file)
index 0000000..7a96977
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# TPM device configuration
+#
+
+menu "TPM devices"
+
+config TCG_TPM
+       tristate "TPM Hardware Support"
+       depends on EXPERIMENTAL && PCI
+       ---help---
+         If you have a TPM security chip in your system, which
+         implements the Trusted Computing Group's specification,
+         say Yes and it will be accessible from within Linux.  For
+         more information see <http://www.trustedcomputinggroup.org>. 
+         An implementation of the Trusted Software Stack (TSS), the 
+         userspace enablement piece of the specification, can be 
+         obtained at: <http://sourceforge.net/projects/trousers>.  To 
+         compile this driver as a module, choose M here; the module 
+         will be called tpm. If unsure, say N.
+
+config TCG_NSC
+       tristate "National Semiconductor TPM Interface"
+       depends on TCG_TPM
+       ---help---
+         If you have a TPM security chip from National Semicondutor 
+         say Yes and it will be accessible from within Linux.  To 
+         compile this driver as a module, choose M here; the module 
+         will be called tpm_nsc.
+
+config TCG_ATMEL
+       tristate "Atmel TPM Interface"
+       depends on TCG_TPM
+       ---help---
+         If you have a TPM security chip from Atmel say Yes and it 
+         will be accessible from within Linux.  To compile this driver 
+         as a module, choose M here; the module will be called tpm_atmel.
+
+endmenu
+
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
new file mode 100644 (file)
index 0000000..736d3df
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the kernel tpm device drivers.
+#
+obj-$(CONFIG_TCG_TPM) += tpm.o
+obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
+obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
+
diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c
new file mode 100644 (file)
index 0000000..8723533
--- /dev/null
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.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, version 2 of the
+ * License.
+ * 
+ * Note, the TPM chip is not interrupt driven (only polling)
+ * and can have very long timeouts (minutes!). Hence the unusual
+ * calls to schedule_timeout.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/spinlock.h>
+#include "tpm.h"
+
+#define        TPM_MINOR                       224     /* officially assigned */
+
+#define        TPM_BUFSIZE                     2048
+
+/* PCI configuration addresses */
+#define        PCI_GEN_PMCON_1                 0xA0
+#define        PCI_GEN1_DEC                    0xE4
+#define        PCI_LPC_EN                      0xE6
+#define        PCI_GEN2_DEC                    0xEC
+
+static LIST_HEAD(tpm_chip_list);
+static DEFINE_SPINLOCK(driver_lock);
+static int dev_mask[32];
+
+static void user_reader_timeout(unsigned long ptr)
+{
+       struct tpm_chip *chip = (struct tpm_chip *) ptr;
+
+       down(&chip->buffer_mutex);
+       atomic_set(&chip->data_pending, 0);
+       memset(chip->data_buffer, 0, TPM_BUFSIZE);
+       up(&chip->buffer_mutex);
+}
+
+void tpm_time_expired(unsigned long ptr)
+{
+       int *exp = (int *) ptr;
+       *exp = 1;
+}
+
+EXPORT_SYMBOL_GPL(tpm_time_expired);
+
+/*
+ * Initialize the LPC bus and enable the TPM ports
+ */
+int tpm_lpc_bus_init(struct pci_dev *pci_dev, u16 base)
+{
+       u32 lpcenable, tmp;
+       int is_lpcm = 0;
+
+       switch (pci_dev->vendor) {
+       case PCI_VENDOR_ID_INTEL:
+               switch (pci_dev->device) {
+               case PCI_DEVICE_ID_INTEL_82801CA_12:
+               case PCI_DEVICE_ID_INTEL_82801DB_12:
+                       is_lpcm = 1;
+                       break;
+               }
+               /* init ICH (enable LPC) */
+               pci_read_config_dword(pci_dev, PCI_GEN1_DEC, &lpcenable);
+               lpcenable |= 0x20000000;
+               pci_write_config_dword(pci_dev, PCI_GEN1_DEC, lpcenable);
+
+               if (is_lpcm) {
+                       pci_read_config_dword(pci_dev, PCI_GEN1_DEC,
+                                             &lpcenable);
+                       if ((lpcenable & 0x20000000) == 0) {
+                               dev_err(&pci_dev->dev,
+                                       "cannot enable LPC\n");
+                               return -ENODEV;
+                       }
+               }
+
+               /* initialize TPM registers */
+               pci_read_config_dword(pci_dev, PCI_GEN2_DEC, &tmp);
+
+               if (!is_lpcm)
+                       tmp = (tmp & 0xFFFF0000) | (base & 0xFFF0);
+               else
+                       tmp =
+                           (tmp & 0xFFFF0000) | (base & 0xFFF0) |
+                           0x00000001;
+
+               pci_write_config_dword(pci_dev, PCI_GEN2_DEC, tmp);
+
+               if (is_lpcm) {
+                       pci_read_config_dword(pci_dev, PCI_GEN_PMCON_1,
+                                             &tmp);
+                       tmp |= 0x00000004;      /* enable CLKRUN */
+                       pci_write_config_dword(pci_dev, PCI_GEN_PMCON_1,
+                                              tmp);
+               }
+               tpm_write_index(0x0D, 0x55);    /* unlock 4F */
+               tpm_write_index(0x0A, 0x00);    /* int disable */
+               tpm_write_index(0x08, base);    /* base addr lo */
+               tpm_write_index(0x09, (base & 0xFF00) >> 8);    /* base addr hi */
+               tpm_write_index(0x0D, 0xAA);    /* lock 4F */
+               break;
+       case PCI_VENDOR_ID_AMD:
+               /* nothing yet */
+               break;
+       }
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_lpc_bus_init);
+
+/*
+ * Internal kernel interface to transmit TPM commands
+ */
+static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
+                           size_t bufsiz)
+{
+       ssize_t len;
+       u32 count;
+       __be32 *native_size;
+
+       native_size = (__force __be32 *) (buf + 2);
+       count = be32_to_cpu(*native_size);
+
+       if (count == 0)
+               return -ENODATA;
+       if (count > bufsiz) {
+               dev_err(&chip->pci_dev->dev,
+                       "invalid count value %x %zx \n", count, bufsiz);
+               return -E2BIG;
+       }
+
+       down(&chip->tpm_mutex);
+
+       if ((len = chip->vendor->send(chip, (u8 *) buf, count)) < 0) {
+               dev_err(&chip->pci_dev->dev,
+                       "tpm_transmit: tpm_send: error %zd\n", len);
+               return len;
+       }
+
+       down(&chip->timer_manipulation_mutex);
+       chip->time_expired = 0;
+       init_timer(&chip->device_timer);
+       chip->device_timer.function = tpm_time_expired;
+       chip->device_timer.expires = jiffies + 2 * 60 * HZ;
+       chip->device_timer.data = (unsigned long) &chip->time_expired;
+       add_timer(&chip->device_timer);
+       up(&chip->timer_manipulation_mutex);
+
+       do {
+               u8 status = inb(chip->vendor->base + 1);
+               if ((status & chip->vendor->req_complete_mask) ==
+                   chip->vendor->req_complete_val) {
+                       down(&chip->timer_manipulation_mutex);
+                       del_singleshot_timer_sync(&chip->device_timer);
+                       up(&chip->timer_manipulation_mutex);
+                       goto out_recv;
+               }
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(TPM_TIMEOUT);
+               rmb();
+       } while (!chip->time_expired);
+
+
+       chip->vendor->cancel(chip);
+       dev_err(&chip->pci_dev->dev, "Time expired\n");
+       up(&chip->tpm_mutex);
+       return -EIO;
+
+out_recv:
+       len = chip->vendor->recv(chip, (u8 *) buf, bufsiz);
+       if (len < 0)
+               dev_err(&chip->pci_dev->dev,
+                       "tpm_transmit: tpm_recv: error %zd\n", len);
+       up(&chip->tpm_mutex);
+       return len;
+}
+
+#define TPM_DIGEST_SIZE 20
+#define CAP_PCR_RESULT_SIZE 18
+static u8 cap_pcr[] = {
+       0, 193,                 /* TPM_TAG_RQU_COMMAND */
+       0, 0, 0, 22,            /* length */
+       0, 0, 0, 101,           /* TPM_ORD_GetCapability */
+       0, 0, 0, 5,
+       0, 0, 0, 4,
+       0, 0, 1, 1
+};
+
+#define READ_PCR_RESULT_SIZE 30
+static u8 pcrread[] = {
+       0, 193,                 /* TPM_TAG_RQU_COMMAND */
+       0, 0, 0, 14,            /* length */
+       0, 0, 0, 21,            /* TPM_ORD_PcrRead */
+       0, 0, 0, 0              /* PCR index */
+};
+
+static ssize_t show_pcrs(struct device *dev, char *buf)
+{
+       u8 data[READ_PCR_RESULT_SIZE];
+       ssize_t len;
+       int i, j, index, num_pcrs;
+       char *str = buf;
+
+       struct tpm_chip *chip =
+           pci_get_drvdata(container_of(dev, struct pci_dev, dev));
+       if (chip == NULL)
+               return -ENODEV;
+
+       memcpy(data, cap_pcr, sizeof(cap_pcr));
+       if ((len = tpm_transmit(chip, data, sizeof(data)))
+           < CAP_PCR_RESULT_SIZE)
+               return len;
+
+       num_pcrs = be32_to_cpu(*((__force __be32 *) (data + 14)));
+
+       for (i = 0; i < num_pcrs; i++) {
+               memcpy(data, pcrread, sizeof(pcrread));
+               index = cpu_to_be32(i);
+               memcpy(data + 10, &index, 4);
+               if ((len = tpm_transmit(chip, data, sizeof(data)))
+                   < READ_PCR_RESULT_SIZE)
+                       return len;
+               str += sprintf(str, "PCR-%02d: ", i);
+               for (j = 0; j < TPM_DIGEST_SIZE; j++)
+                       str += sprintf(str, "%02X ", *(data + 10 + j));
+               str += sprintf(str, "\n");
+       }
+       return str - buf;
+}
+
+static DEVICE_ATTR(pcrs, S_IRUGO, show_pcrs, NULL);
+
+#define  READ_PUBEK_RESULT_SIZE 314
+static u8 readpubek[] = {
+       0, 193,                 /* TPM_TAG_RQU_COMMAND */
+       0, 0, 0, 30,            /* length */
+       0, 0, 0, 124,           /* TPM_ORD_ReadPubek */
+};
+
+static ssize_t show_pubek(struct device *dev, char *buf)
+{
+       u8 data[READ_PUBEK_RESULT_SIZE];
+       ssize_t len;
+       __be32 *native_val;
+       int i;
+       char *str = buf;
+
+       struct tpm_chip *chip =
+           pci_get_drvdata(container_of(dev, struct pci_dev, dev));
+       if (chip == NULL)
+               return -ENODEV;
+
+       memcpy(data, readpubek, sizeof(readpubek));
+       memset(data + sizeof(readpubek), 0, 20);        /* zero nonce */
+
+       if ((len = tpm_transmit(chip, data, sizeof(data))) <
+           READ_PUBEK_RESULT_SIZE)
+               return len;
+
+       /* 
+          ignore header 10 bytes
+          algorithm 32 bits (1 == RSA )
+          encscheme 16 bits
+          sigscheme 16 bits
+          parameters (RSA 12->bytes: keybit, #primes, expbit)  
+          keylenbytes 32 bits
+          256 byte modulus
+          ignore checksum 20 bytes
+        */
+
+       native_val = (__force __be32 *) (data + 34);
+
+       str +=
+           sprintf(str,
+                   "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n"
+                   "Sigscheme: %02X %02X\nParameters: %02X %02X %02X %02X"
+                   " %02X %02X %02X %02X %02X %02X %02X %02X\n"
+                   "Modulus length: %d\nModulus: \n",
+                   data[10], data[11], data[12], data[13], data[14],
+                   data[15], data[16], data[17], data[22], data[23],
+                   data[24], data[25], data[26], data[27], data[28],
+                   data[29], data[30], data[31], data[32], data[33],
+                   be32_to_cpu(*native_val)
+           );
+
+       for (i = 0; i < 256; i++) {
+               str += sprintf(str, "%02X ", data[i + 39]);
+               if ((i + 1) % 16 == 0)
+                       str += sprintf(str, "\n");
+       }
+       return str - buf;
+}
+
+static DEVICE_ATTR(pubek, S_IRUGO, show_pubek, NULL);
+
+#define CAP_VER_RESULT_SIZE 18
+static u8 cap_version[] = {
+       0, 193,                 /* TPM_TAG_RQU_COMMAND */
+       0, 0, 0, 18,            /* length */
+       0, 0, 0, 101,           /* TPM_ORD_GetCapability */
+       0, 0, 0, 6,
+       0, 0, 0, 0
+};
+
+#define CAP_MANUFACTURER_RESULT_SIZE 18
+static u8 cap_manufacturer[] = {
+       0, 193,                 /* TPM_TAG_RQU_COMMAND */
+       0, 0, 0, 22,            /* length */
+       0, 0, 0, 101,           /* TPM_ORD_GetCapability */
+       0, 0, 0, 5,
+       0, 0, 0, 4,
+       0, 0, 1, 3
+};
+
+static ssize_t show_caps(struct device *dev, char *buf)
+{
+       u8 data[READ_PUBEK_RESULT_SIZE];
+       ssize_t len;
+       char *str = buf;
+
+       struct tpm_chip *chip =
+           pci_get_drvdata(container_of(dev, struct pci_dev, dev));
+       if (chip == NULL)
+               return -ENODEV;
+
+       memcpy(data, cap_manufacturer, sizeof(cap_manufacturer));
+
+       if ((len = tpm_transmit(chip, data, sizeof(data))) <
+           CAP_MANUFACTURER_RESULT_SIZE)
+               return len;
+
+       str += sprintf(str, "Manufacturer: 0x%x\n",
+                      be32_to_cpu(*(data + 14)));
+
+       memcpy(data, cap_version, sizeof(cap_version));
+
+       if ((len = tpm_transmit(chip, data, sizeof(data))) <
+           CAP_VER_RESULT_SIZE)
+               return len;
+
+       str +=
+           sprintf(str, "TCG version: %d.%d\nFirmware version: %d.%d\n",
+                   (int) data[14], (int) data[15], (int) data[16],
+                   (int) data[17]);
+
+       return str - buf;
+}
+
+static DEVICE_ATTR(caps, S_IRUGO, show_caps, NULL);
+
+/*
+ * Device file system interface to the TPM
+ */
+int tpm_open(struct inode *inode, struct file *file)
+{
+       int rc = 0, minor = iminor(inode);
+       struct tpm_chip *chip = NULL, *pos;
+
+       spin_lock(&driver_lock);
+
+       list_for_each_entry(pos, &tpm_chip_list, list) {
+               if (pos->vendor->miscdev.minor == minor) {
+                       chip = pos;
+                       break;
+               }
+       }
+
+       if (chip == NULL) {
+               rc = -ENODEV;
+               goto err_out;
+       }
+
+       if (chip->num_opens) {
+               dev_dbg(&chip->pci_dev->dev,
+                       "Another process owns this TPM\n");
+               rc = -EBUSY;
+               goto err_out;
+       }
+
+       chip->num_opens++;
+       pci_dev_get(chip->pci_dev);
+
+       spin_unlock(&driver_lock);
+
+       chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
+       if (chip->data_buffer == NULL) {
+               chip->num_opens--;
+               pci_dev_put(chip->pci_dev);
+               return -ENOMEM;
+       }
+
+       atomic_set(&chip->data_pending, 0);
+
+       file->private_data = chip;
+       return 0;
+
+err_out:
+       spin_unlock(&driver_lock);
+       return rc;
+}
+
+EXPORT_SYMBOL_GPL(tpm_open);
+
+int tpm_release(struct inode *inode, struct file *file)
+{
+       struct tpm_chip *chip = file->private_data;
+       
+       file->private_data = NULL;
+
+       spin_lock(&driver_lock);
+       chip->num_opens--;
+       spin_unlock(&driver_lock);
+
+       down(&chip->timer_manipulation_mutex);
+       if (timer_pending(&chip->user_read_timer))
+               del_singleshot_timer_sync(&chip->user_read_timer);
+       else if (timer_pending(&chip->device_timer))
+               del_singleshot_timer_sync(&chip->device_timer);
+       up(&chip->timer_manipulation_mutex);
+
+       kfree(chip->data_buffer);
+       atomic_set(&chip->data_pending, 0);
+
+       pci_dev_put(chip->pci_dev);
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_release);
+
+ssize_t tpm_write(struct file * file, const char __user * buf,
+                 size_t size, loff_t * off)
+{
+       struct tpm_chip *chip = file->private_data;
+       int in_size = size, out_size;
+
+       /* cannot perform a write until the read has cleared
+          either via tpm_read or a user_read_timer timeout */
+       while (atomic_read(&chip->data_pending) != 0) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(TPM_TIMEOUT);
+       }
+
+       down(&chip->buffer_mutex);
+
+       if (in_size > TPM_BUFSIZE)
+               in_size = TPM_BUFSIZE;
+
+       if (copy_from_user
+           (chip->data_buffer, (void __user *) buf, in_size)) {
+               up(&chip->buffer_mutex);
+               return -EFAULT;
+       }
+
+       /* atomic tpm command send and result receive */
+       out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE);
+
+       atomic_set(&chip->data_pending, out_size);
+       up(&chip->buffer_mutex);
+
+       /* Set a timeout by which the reader must come claim the result */
+       down(&chip->timer_manipulation_mutex);
+       init_timer(&chip->user_read_timer);
+       chip->user_read_timer.function = user_reader_timeout;
+       chip->user_read_timer.data = (unsigned long) chip;
+       chip->user_read_timer.expires = jiffies + (60 * HZ);
+       add_timer(&chip->user_read_timer);
+       up(&chip->timer_manipulation_mutex);
+
+       return in_size;
+}
+
+EXPORT_SYMBOL_GPL(tpm_write);
+
+ssize_t tpm_read(struct file * file, char __user * buf,
+                size_t size, loff_t * off)
+{
+       struct tpm_chip *chip = file->private_data;
+       int ret_size = -ENODATA;
+
+       if (atomic_read(&chip->data_pending) != 0) {    /* Result available */
+               down(&chip->timer_manipulation_mutex);
+               del_singleshot_timer_sync(&chip->user_read_timer);
+               up(&chip->timer_manipulation_mutex);
+
+               down(&chip->buffer_mutex);
+
+               ret_size = atomic_read(&chip->data_pending);
+               atomic_set(&chip->data_pending, 0);
+
+               if (ret_size == 0)      /* timeout just occurred */
+                       ret_size = -ETIME;
+               else if (ret_size > 0) {        /* relay data */
+                       if (size < ret_size)
+                               ret_size = size;
+
+                       if (copy_to_user((void __user *) buf,
+                                        chip->data_buffer, ret_size)) {
+                               ret_size = -EFAULT;
+                       }
+               }
+               up(&chip->buffer_mutex);
+       }
+
+       return ret_size;
+}
+
+EXPORT_SYMBOL_GPL(tpm_read);
+
+void __devexit tpm_remove(struct pci_dev *pci_dev)
+{
+       struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+
+       if (chip == NULL) {
+               dev_err(&pci_dev->dev, "No device data found\n");
+               return;
+       }
+
+       spin_lock(&driver_lock);
+
+       list_del(&chip->list);
+
+       spin_unlock(&driver_lock);
+
+       pci_set_drvdata(pci_dev, NULL);
+       misc_deregister(&chip->vendor->miscdev);
+
+       device_remove_file(&pci_dev->dev, &dev_attr_pubek);
+       device_remove_file(&pci_dev->dev, &dev_attr_pcrs);
+       device_remove_file(&pci_dev->dev, &dev_attr_caps);
+
+       pci_disable_device(pci_dev);
+
+       dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
+
+       kfree(chip);
+
+       pci_dev_put(pci_dev);
+}
+
+EXPORT_SYMBOL_GPL(tpm_remove);
+
+static u8 savestate[] = {
+       0, 193,                 /* TPM_TAG_RQU_COMMAND */
+       0, 0, 0, 10,            /* blob length (in bytes) */
+       0, 0, 0, 152            /* TPM_ORD_SaveState */
+};
+
+/*
+ * We are about to suspend. Save the TPM state
+ * so that it can be restored.
+ */
+int tpm_pm_suspend(struct pci_dev *pci_dev, pm_message_t pm_state)
+{
+       struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+       if (chip == NULL)
+               return -ENODEV;
+
+       tpm_transmit(chip, savestate, sizeof(savestate));
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_pm_suspend);
+
+/*
+ * Resume from a power safe. The BIOS already restored
+ * the TPM state.
+ */
+int tpm_pm_resume(struct pci_dev *pci_dev)
+{
+       struct tpm_chip *chip = pci_get_drvdata(pci_dev);
+
+       if (chip == NULL)
+               return -ENODEV;
+
+       spin_lock(&driver_lock);
+       tpm_lpc_bus_init(pci_dev, chip->vendor->base);
+       spin_unlock(&driver_lock);
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_pm_resume);
+
+/*
+ * Called from tpm_<specific>.c probe function only for devices 
+ * the driver has determined it should claim.  Prior to calling
+ * this function the specific probe function has called pci_enable_device
+ * upon errant exit from this function specific probe function should call
+ * pci_disable_device
+ */
+int tpm_register_hardware(struct pci_dev *pci_dev,
+                         struct tpm_vendor_specific *entry)
+{
+       char devname[7];
+       struct tpm_chip *chip;
+       int i, j;
+
+       /* Driver specific per-device data */
+       chip = kmalloc(sizeof(*chip), GFP_KERNEL);
+       if (chip == NULL)
+               return -ENOMEM;
+
+       memset(chip, 0, sizeof(struct tpm_chip));
+
+       init_MUTEX(&chip->buffer_mutex);
+       init_MUTEX(&chip->tpm_mutex);
+       init_MUTEX(&chip->timer_manipulation_mutex);
+       INIT_LIST_HEAD(&chip->list);
+
+       chip->vendor = entry;
+
+       chip->dev_num = -1;
+
+       for (i = 0; i < 32; i++)
+               for (j = 0; j < 8; j++)
+                       if ((dev_mask[i] & (1 << j)) == 0) {
+                               chip->dev_num = i * 32 + j;
+                               dev_mask[i] |= 1 << j;
+                               goto dev_num_search_complete;
+                       }
+
+dev_num_search_complete:
+       if (chip->dev_num < 0) {
+               dev_err(&pci_dev->dev,
+                       "No available tpm device numbers\n");
+               kfree(chip);
+               return -ENODEV;
+       } else if (chip->dev_num == 0)
+               chip->vendor->miscdev.minor = TPM_MINOR;
+       else
+               chip->vendor->miscdev.minor = MISC_DYNAMIC_MINOR;
+
+       snprintf(devname, sizeof(devname), "%s%d", "tpm", chip->dev_num);
+       chip->vendor->miscdev.name = devname;
+
+       chip->vendor->miscdev.dev = &(pci_dev->dev);
+       chip->pci_dev = pci_dev_get(pci_dev);
+
+       if (misc_register(&chip->vendor->miscdev)) {
+               dev_err(&chip->pci_dev->dev,
+                       "unable to misc_register %s, minor %d\n",
+                       chip->vendor->miscdev.name,
+                       chip->vendor->miscdev.minor);
+               pci_dev_put(pci_dev);
+               kfree(chip);
+               dev_mask[i] &= !(1 << j);
+               return -ENODEV;
+       }
+
+       pci_set_drvdata(pci_dev, chip);
+
+       list_add(&chip->list, &tpm_chip_list);
+
+       device_create_file(&pci_dev->dev, &dev_attr_pubek);
+       device_create_file(&pci_dev->dev, &dev_attr_pcrs);
+       device_create_file(&pci_dev->dev, &dev_attr_caps);
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(tpm_register_hardware);
+
+static int __init init_tpm(void)
+{
+       return 0;
+}
+
+static void __exit cleanup_tpm(void)
+{
+
+}
+
+module_init(init_tpm);
+module_exit(cleanup_tpm);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
new file mode 100644 (file)
index 0000000..de0c796
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.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, version 2 of the
+ * License.
+ * 
+ */
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+
+#define TPM_TIMEOUT msecs_to_jiffies(5)
+
+/* TPM addresses */
+#define        TPM_ADDR                        0x4E
+#define        TPM_DATA                        0x4F
+
+struct tpm_chip;
+
+struct tpm_vendor_specific {
+       u8 req_complete_mask;
+       u8 req_complete_val;
+       u16 base;               /* TPM base address */
+
+       int (*recv) (struct tpm_chip *, u8 *, size_t);
+       int (*send) (struct tpm_chip *, u8 *, size_t);
+       void (*cancel) (struct tpm_chip *);
+       struct miscdevice miscdev;
+};
+
+struct tpm_chip {
+       struct pci_dev *pci_dev;        /* PCI device stuff */
+
+       int dev_num;            /* /dev/tpm# */
+       int num_opens;          /* only one allowed */
+       int time_expired;
+
+       /* Data passed to and from the tpm via the read/write calls */
+       u8 *data_buffer;
+       atomic_t data_pending;
+       struct semaphore buffer_mutex;
+
+       struct timer_list user_read_timer;      /* user needs to claim result */
+       struct semaphore tpm_mutex;     /* tpm is processing */
+       struct timer_list device_timer; /* tpm is processing */
+       struct semaphore timer_manipulation_mutex;
+
+       struct tpm_vendor_specific *vendor;
+
+       struct list_head list;
+};
+
+static inline int tpm_read_index(int index)
+{
+       outb(index, TPM_ADDR);
+       return inb(TPM_DATA) & 0xFF;
+}
+
+static inline void tpm_write_index(int index, int value)
+{
+       outb(index, TPM_ADDR);
+       outb(value & 0xFF, TPM_DATA);
+}
+
+extern void tpm_time_expired(unsigned long);
+extern int tpm_lpc_bus_init(struct pci_dev *, u16);
+
+extern int tpm_register_hardware(struct pci_dev *,
+                                struct tpm_vendor_specific *);
+extern int tpm_open(struct inode *, struct file *);
+extern int tpm_release(struct inode *, struct file *);
+extern ssize_t tpm_write(struct file *, const char __user *, size_t,
+                        loff_t *);
+extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *);
+extern void __devexit tpm_remove(struct pci_dev *);
+extern int tpm_pm_suspend(struct pci_dev *, pm_message_t);
+extern int tpm_pm_resume(struct pci_dev *);
diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c
new file mode 100644 (file)
index 0000000..f9333e7
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.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, version 2 of the
+ * License.
+ * 
+ */
+
+#include "tpm.h"
+
+/* Atmel definitions */
+#define        TPM_ATML_BASE                   0x400
+
+/* write status bits */
+#define        ATML_STATUS_ABORT               0x01
+#define        ATML_STATUS_LASTBYTE            0x04
+
+/* read status bits */
+#define        ATML_STATUS_BUSY                0x01
+#define        ATML_STATUS_DATA_AVAIL          0x02
+#define        ATML_STATUS_REWRITE             0x04
+
+
+static int tpm_atml_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+       u8 status, *hdr = buf;
+       u32 size;
+       int i;
+       __be32 *native_size;
+
+       /* start reading header */
+       if (count < 6)
+               return -EIO;
+
+       for (i = 0; i < 6; i++) {
+               status = inb(chip->vendor->base + 1);
+               if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+                       dev_err(&chip->pci_dev->dev,
+                               "error reading header\n");
+                       return -EIO;
+               }
+               *buf++ = inb(chip->vendor->base);
+       }
+
+       /* size of the data received */
+       native_size = (__force __be32 *) (hdr + 2);
+       size = be32_to_cpu(*native_size);
+
+       if (count < size) {
+               dev_err(&chip->pci_dev->dev,
+                       "Recv size(%d) less than available space\n", size);
+               for (; i < size; i++) { /* clear the waiting data anyway */
+                       status = inb(chip->vendor->base + 1);
+                       if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+                               dev_err(&chip->pci_dev->dev,
+                                       "error reading data\n");
+                               return -EIO;
+                       }
+               }
+               return -EIO;
+       }
+
+       /* read all the data available */
+       for (; i < size; i++) {
+               status = inb(chip->vendor->base + 1);
+               if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
+                       dev_err(&chip->pci_dev->dev,
+                               "error reading data\n");
+                       return -EIO;
+               }
+               *buf++ = inb(chip->vendor->base);
+       }
+
+       /* make sure data available is gone */
+       status = inb(chip->vendor->base + 1);
+       if (status & ATML_STATUS_DATA_AVAIL) {
+               dev_err(&chip->pci_dev->dev, "data available is stuck\n");
+               return -EIO;
+       }
+
+       return size;
+}
+
+static int tpm_atml_send(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+       int i;
+
+       dev_dbg(&chip->pci_dev->dev, "tpm_atml_send: ");
+       for (i = 0; i < count; i++) {
+               dev_dbg(&chip->pci_dev->dev, "0x%x(%d) ", buf[i], buf[i]);
+               outb(buf[i], chip->vendor->base);
+       }
+
+       return count;
+}
+
+static void tpm_atml_cancel(struct tpm_chip *chip)
+{
+       outb(ATML_STATUS_ABORT, chip->vendor->base + 1);
+}
+
+static struct file_operations atmel_ops = {
+       .owner = THIS_MODULE,
+       .llseek = no_llseek,
+       .open = tpm_open,
+       .read = tpm_read,
+       .write = tpm_write,
+       .release = tpm_release,
+};
+
+static struct tpm_vendor_specific tpm_atmel = {
+       .recv = tpm_atml_recv,
+       .send = tpm_atml_send,
+       .cancel = tpm_atml_cancel,
+       .req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
+       .req_complete_val = ATML_STATUS_DATA_AVAIL,
+       .base = TPM_ATML_BASE,
+       .miscdev = { .fops = &atmel_ops, },
+};
+
+static int __devinit tpm_atml_init(struct pci_dev *pci_dev,
+                                  const struct pci_device_id *pci_id)
+{
+       u8 version[4];
+       int rc = 0;
+
+       if (pci_enable_device(pci_dev))
+               return -EIO;
+
+       if (tpm_lpc_bus_init(pci_dev, TPM_ATML_BASE)) {
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       /* verify that it is an Atmel part */
+       if (tpm_read_index(4) != 'A' || tpm_read_index(5) != 'T'
+           || tpm_read_index(6) != 'M' || tpm_read_index(7) != 'L') {
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       /* query chip for its version number */
+       if ((version[0] = tpm_read_index(0x00)) != 0xFF) {
+               version[1] = tpm_read_index(0x01);
+               version[2] = tpm_read_index(0x02);
+               version[3] = tpm_read_index(0x03);
+       } else {
+               dev_info(&pci_dev->dev, "version query failed\n");
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       if ((rc = tpm_register_hardware(pci_dev, &tpm_atmel)) < 0)
+               goto out_err;
+
+       dev_info(&pci_dev->dev,
+                "Atmel TPM version %d.%d.%d.%d\n", version[0], version[1],
+                version[2], version[3]);
+
+       return 0;
+out_err:
+       pci_disable_device(pci_dev);
+       return rc;
+}
+
+static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
+       {PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
+       {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
+
+static struct pci_driver atmel_pci_driver = {
+       .name = "tpm_atmel",
+       .id_table = tpm_pci_tbl,
+       .probe = tpm_atml_init,
+       .remove = __devexit_p(tpm_remove),
+       .suspend = tpm_pm_suspend,
+       .resume = tpm_pm_resume,
+};
+
+static int __init init_atmel(void)
+{
+       return pci_register_driver(&atmel_pci_driver);
+}
+
+static void __exit cleanup_atmel(void)
+{
+       pci_unregister_driver(&atmel_pci_driver);
+}
+
+module_init(init_atmel);
+module_exit(cleanup_atmel);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c
new file mode 100644 (file)
index 0000000..9cce833
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2004 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.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, version 2 of the
+ * License.
+ * 
+ */
+
+#include "tpm.h"
+
+/* National definitions */
+#define        TPM_NSC_BASE                    0x360
+#define        TPM_NSC_IRQ                     0x07
+
+#define        NSC_LDN_INDEX                   0x07
+#define        NSC_SID_INDEX                   0x20
+#define        NSC_LDC_INDEX                   0x30
+#define        NSC_DIO_INDEX                   0x60
+#define        NSC_CIO_INDEX                   0x62
+#define        NSC_IRQ_INDEX                   0x70
+#define        NSC_ITS_INDEX                   0x71
+
+#define        NSC_STATUS                      0x01
+#define        NSC_COMMAND                     0x01
+#define        NSC_DATA                        0x00
+
+/* status bits */
+#define        NSC_STATUS_OBF                  0x01    /* output buffer full */
+#define        NSC_STATUS_IBF                  0x02    /* input buffer full */
+#define        NSC_STATUS_F0                   0x04    /* F0 */
+#define        NSC_STATUS_A2                   0x08    /* A2 */
+#define        NSC_STATUS_RDY                  0x10    /* ready to receive command */
+#define        NSC_STATUS_IBR                  0x20    /* ready to receive data */
+
+/* command bits */
+#define        NSC_COMMAND_NORMAL              0x01    /* normal mode */
+#define        NSC_COMMAND_EOC                 0x03
+#define        NSC_COMMAND_CANCEL              0x22
+
+/*
+ * Wait for a certain status to appear
+ */
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
+{
+       int expired = 0;
+       struct timer_list status_timer =
+           TIMER_INITIALIZER(tpm_time_expired, jiffies + 10 * HZ,
+                             (unsigned long) &expired);
+
+       /* status immediately available check */
+       *data = inb(chip->vendor->base + NSC_STATUS);
+       if ((*data & mask) == val)
+               return 0;
+
+       /* wait for status */
+       add_timer(&status_timer);
+       do {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(TPM_TIMEOUT);
+               *data = inb(chip->vendor->base + 1);
+               if ((*data & mask) == val) {
+                       del_singleshot_timer_sync(&status_timer);
+                       return 0;
+               }
+       }
+       while (!expired);
+
+       return -EBUSY;
+}
+
+static int nsc_wait_for_ready(struct tpm_chip *chip)
+{
+       int status;
+       int expired = 0;
+       struct timer_list status_timer =
+           TIMER_INITIALIZER(tpm_time_expired, jiffies + 100,
+                             (unsigned long) &expired);
+
+       /* status immediately available check */
+       status = inb(chip->vendor->base + NSC_STATUS);
+       if (status & NSC_STATUS_OBF)
+               status = inb(chip->vendor->base + NSC_DATA);
+       if (status & NSC_STATUS_RDY)
+               return 0;
+
+       /* wait for status */
+       add_timer(&status_timer);
+       do {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(TPM_TIMEOUT);
+               status = inb(chip->vendor->base + NSC_STATUS);
+               if (status & NSC_STATUS_OBF)
+                       status = inb(chip->vendor->base + NSC_DATA);
+               if (status & NSC_STATUS_RDY) {
+                       del_singleshot_timer_sync(&status_timer);
+                       return 0;
+               }
+       }
+       while (!expired);
+
+       dev_info(&chip->pci_dev->dev, "wait for ready failed\n");
+       return -EBUSY;
+}
+
+
+static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+       u8 *buffer = buf;
+       u8 data, *p;
+       u32 size;
+       __be32 *native_size;
+
+       if (count < 6)
+               return -EIO;
+
+       if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
+               dev_err(&chip->pci_dev->dev, "F0 timeout\n");
+               return -EIO;
+       }
+       if ((data =
+            inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
+               dev_err(&chip->pci_dev->dev, "not in normal mode (0x%x)\n",
+                       data);
+               return -EIO;
+       }
+
+       /* read the whole packet */
+       for (p = buffer; p < &buffer[count]; p++) {
+               if (wait_for_stat
+                   (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
+                       dev_err(&chip->pci_dev->dev,
+                               "OBF timeout (while reading data)\n");
+                       return -EIO;
+               }
+               if (data & NSC_STATUS_F0)
+                       break;
+               *p = inb(chip->vendor->base + NSC_DATA);
+       }
+
+       if ((data & NSC_STATUS_F0) == 0) {
+               dev_err(&chip->pci_dev->dev, "F0 not set\n");
+               return -EIO;
+       }
+       if ((data = inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_EOC) {
+               dev_err(&chip->pci_dev->dev,
+                       "expected end of command(0x%x)\n", data);
+               return -EIO;
+       }
+
+       native_size = (__force __be32 *) (buf + 2);
+       size = be32_to_cpu(*native_size);
+
+       if (count < size)
+               return -EIO;
+
+       return size;
+}
+
+static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
+{
+       u8 data;
+       int i;
+
+       /*
+        * If we hit the chip with back to back commands it locks up
+        * and never set IBF. Hitting it with this "hammer" seems to
+        * fix it. Not sure why this is needed, we followed the flow
+        * chart in the manual to the letter.
+        */
+       outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND);
+
+       if (nsc_wait_for_ready(chip) != 0)
+               return -EIO;
+
+       if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+               dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+               return -EIO;
+       }
+
+       outb(NSC_COMMAND_NORMAL, chip->vendor->base + NSC_COMMAND);
+       if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
+               dev_err(&chip->pci_dev->dev, "IBR timeout\n");
+               return -EIO;
+       }
+
+       for (i = 0; i < count; i++) {
+               if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+                       dev_err(&chip->pci_dev->dev,
+                               "IBF timeout (while writing data)\n");
+                       return -EIO;
+               }
+               outb(buf[i], chip->vendor->base + NSC_DATA);
+       }
+
+       if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
+               dev_err(&chip->pci_dev->dev, "IBF timeout\n");
+               return -EIO;
+       }
+       outb(NSC_COMMAND_EOC, chip->vendor->base + NSC_COMMAND);
+
+       return count;
+}
+
+static void tpm_nsc_cancel(struct tpm_chip *chip)
+{
+       outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND);
+}
+
+static struct file_operations nsc_ops = {
+       .owner = THIS_MODULE,
+       .llseek = no_llseek,
+       .open = tpm_open,
+       .read = tpm_read,
+       .write = tpm_write,
+       .release = tpm_release,
+};
+
+static struct tpm_vendor_specific tpm_nsc = {
+       .recv = tpm_nsc_recv,
+       .send = tpm_nsc_send,
+       .cancel = tpm_nsc_cancel,
+       .req_complete_mask = NSC_STATUS_OBF,
+       .req_complete_val = NSC_STATUS_OBF,
+       .base = TPM_NSC_BASE,
+       .miscdev = { .fops = &nsc_ops, },
+       
+};
+
+static int __devinit tpm_nsc_init(struct pci_dev *pci_dev,
+                                 const struct pci_device_id *pci_id)
+{
+       int rc = 0;
+
+       if (pci_enable_device(pci_dev))
+               return -EIO;
+
+       if (tpm_lpc_bus_init(pci_dev, TPM_NSC_BASE)) {
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       /* verify that it is a National part (SID) */
+       if (tpm_read_index(NSC_SID_INDEX) != 0xEF) {
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       dev_dbg(&pci_dev->dev, "NSC TPM detected\n");
+       dev_dbg(&pci_dev->dev,
+               "NSC LDN 0x%x, SID 0x%x, SRID 0x%x\n",
+               tpm_read_index(0x07), tpm_read_index(0x20),
+               tpm_read_index(0x27));
+       dev_dbg(&pci_dev->dev,
+               "NSC SIOCF1 0x%x SIOCF5 0x%x SIOCF6 0x%x SIOCF8 0x%x\n",
+               tpm_read_index(0x21), tpm_read_index(0x25),
+               tpm_read_index(0x26), tpm_read_index(0x28));
+       dev_dbg(&pci_dev->dev, "NSC IO Base0 0x%x\n",
+               (tpm_read_index(0x60) << 8) | tpm_read_index(0x61));
+       dev_dbg(&pci_dev->dev, "NSC IO Base1 0x%x\n",
+               (tpm_read_index(0x62) << 8) | tpm_read_index(0x63));
+       dev_dbg(&pci_dev->dev, "NSC Interrupt number and wakeup 0x%x\n",
+               tpm_read_index(0x70));
+       dev_dbg(&pci_dev->dev, "NSC IRQ type select 0x%x\n",
+               tpm_read_index(0x71));
+       dev_dbg(&pci_dev->dev,
+               "NSC DMA channel select0 0x%x, select1 0x%x\n",
+               tpm_read_index(0x74), tpm_read_index(0x75));
+       dev_dbg(&pci_dev->dev,
+               "NSC Config "
+               "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+               tpm_read_index(0xF0), tpm_read_index(0xF1),
+               tpm_read_index(0xF2), tpm_read_index(0xF3),
+               tpm_read_index(0xF4), tpm_read_index(0xF5),
+               tpm_read_index(0xF6), tpm_read_index(0xF7),
+               tpm_read_index(0xF8), tpm_read_index(0xF9));
+
+       dev_info(&pci_dev->dev,
+                "NSC PC21100 TPM revision %d\n",
+                tpm_read_index(0x27) & 0x1F);
+
+       if (tpm_read_index(NSC_LDC_INDEX) == 0)
+               dev_info(&pci_dev->dev, ": NSC TPM not active\n");
+
+       /* select PM channel 1 */
+       tpm_write_index(NSC_LDN_INDEX, 0x12);
+       tpm_read_index(NSC_LDN_INDEX);
+
+       /* disable the DPM module */
+       tpm_write_index(NSC_LDC_INDEX, 0);
+       tpm_read_index(NSC_LDC_INDEX);
+
+       /* set the data register base addresses */
+       tpm_write_index(NSC_DIO_INDEX, TPM_NSC_BASE >> 8);
+       tpm_write_index(NSC_DIO_INDEX + 1, TPM_NSC_BASE);
+       tpm_read_index(NSC_DIO_INDEX);
+       tpm_read_index(NSC_DIO_INDEX + 1);
+
+       /* set the command register base addresses */
+       tpm_write_index(NSC_CIO_INDEX, (TPM_NSC_BASE + 1) >> 8);
+       tpm_write_index(NSC_CIO_INDEX + 1, (TPM_NSC_BASE + 1));
+       tpm_read_index(NSC_DIO_INDEX);
+       tpm_read_index(NSC_DIO_INDEX + 1);
+
+       /* set the interrupt number to be used for the host interface */
+       tpm_write_index(NSC_IRQ_INDEX, TPM_NSC_IRQ);
+       tpm_write_index(NSC_ITS_INDEX, 0x00);
+       tpm_read_index(NSC_IRQ_INDEX);
+
+       /* enable the DPM module */
+       tpm_write_index(NSC_LDC_INDEX, 0x01);
+       tpm_read_index(NSC_LDC_INDEX);
+
+       if ((rc = tpm_register_hardware(pci_dev, &tpm_nsc)) < 0)
+               goto out_err;
+
+       return 0;
+
+out_err:
+       pci_disable_device(pci_dev);
+       return rc;
+}
+
+static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
+       {PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
+       {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
+
+static struct pci_driver nsc_pci_driver = {
+       .name = "tpm_nsc",
+       .id_table = tpm_pci_tbl,
+       .probe = tpm_nsc_init,
+       .remove = __devexit_p(tpm_remove),
+       .suspend = tpm_pm_suspend,
+       .resume = tpm_pm_resume,
+};
+
+static int __init init_nsc(void)
+{
+       return pci_register_driver(&nsc_pci_driver);
+}
+
+static void __exit cleanup_nsc(void)
+{
+       pci_unregister_driver(&nsc_pci_driver);
+}
+
+module_init(init_nsc);
+module_exit(cleanup_nsc);
+
+MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
+MODULE_DESCRIPTION("TPM Driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/vr41xx_rtc.c b/drivers/char/vr41xx_rtc.c
new file mode 100644 (file)
index 0000000..a6dbe4d
--- /dev/null
@@ -0,0 +1,709 @@
+/*
+ *  Driver for NEC VR4100 series  Real Time Clock unit.
+ *
+ *  Copyright (C) 2003-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/device.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/mc146818rtc.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/rtc.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include <asm/div64.h>
+#include <asm/io.h>
+#include <asm/time.h>
+#include <asm/uaccess.h>
+#include <asm/vr41xx/vr41xx.h>
+
+MODULE_AUTHOR("Yoichi Yuasa <yuasa@hh.iij4u.or.jp>");
+MODULE_DESCRIPTION("NEC VR4100 series RTC driver");
+MODULE_LICENSE("GPL");
+
+#define RTC1_TYPE1_START       0x0b0000c0UL
+#define RTC1_TYPE1_END         0x0b0000dfUL
+#define RTC2_TYPE1_START       0x0b0001c0UL
+#define RTC2_TYPE1_END         0x0b0001dfUL
+
+#define RTC1_TYPE2_START       0x0f000100UL
+#define RTC1_TYPE2_END         0x0f00011fUL
+#define RTC2_TYPE2_START       0x0f000120UL
+#define RTC2_TYPE2_END         0x0f00013fUL
+
+#define RTC1_SIZE              0x20
+#define RTC2_SIZE              0x20
+
+/* RTC 1 registers */
+#define ETIMELREG              0x00
+#define ETIMEMREG              0x02
+#define ETIMEHREG              0x04
+/* RFU */
+#define ECMPLREG               0x08
+#define ECMPMREG               0x0a
+#define ECMPHREG               0x0c
+/* RFU */
+#define RTCL1LREG              0x10
+#define RTCL1HREG              0x12
+#define RTCL1CNTLREG           0x14
+#define RTCL1CNTHREG           0x16
+#define RTCL2LREG              0x18
+#define RTCL2HREG              0x1a
+#define RTCL2CNTLREG           0x1c
+#define RTCL2CNTHREG           0x1e
+
+/* RTC 2 registers */
+#define TCLKLREG               0x00
+#define TCLKHREG               0x02
+#define TCLKCNTLREG            0x04
+#define TCLKCNTHREG            0x06
+/* RFU */
+#define RTCINTREG              0x1e
+ #define TCLOCK_INT            0x08
+ #define RTCLONG2_INT          0x04
+ #define RTCLONG1_INT          0x02
+ #define ELAPSEDTIME_INT       0x01
+
+#define RTC_FREQUENCY          32768
+#define MAX_PERIODIC_RATE      6553
+#define MAX_USER_PERIODIC_RATE 64
+
+static void __iomem *rtc1_base;
+static void __iomem *rtc2_base;
+
+#define rtc1_read(offset)              readw(rtc1_base + (offset))
+#define rtc1_write(offset, value)      writew((value), rtc1_base + (offset))
+
+#define rtc2_read(offset)              readw(rtc2_base + (offset))
+#define rtc2_write(offset, value)      writew((value), rtc2_base + (offset))
+
+static unsigned long epoch = 1970;     /* Jan 1 1970 00:00:00 */
+
+static spinlock_t rtc_task_lock;
+static wait_queue_head_t rtc_wait;
+static unsigned long rtc_irq_data;
+static struct fasync_struct *rtc_async_queue;
+static rtc_task_t *rtc_callback;
+static char rtc_name[] = "RTC";
+static unsigned long periodic_frequency;
+static unsigned long periodic_count;
+
+typedef enum {
+       RTC_RELEASE,
+       RTC_OPEN,
+} rtc_status_t;
+
+static rtc_status_t rtc_status;
+
+typedef enum {
+       FUNCTION_RTC_IOCTL,
+       FUNCTION_RTC_CONTROL,
+} rtc_callfrom_t;
+
+struct resource rtc_resource[2] = {
+       {       .name   = rtc_name,
+               .flags  = IORESOURCE_MEM,       },
+       {       .name   = rtc_name,
+               .flags  = IORESOURCE_MEM,       },
+};
+
+#define RTC_NUM_RESOURCES      sizeof(rtc_resource) / sizeof(struct resource)
+
+static inline unsigned long read_elapsed_second(void)
+{
+       unsigned long first_low, first_mid, first_high;
+       unsigned long second_low, second_mid, second_high;
+
+       do {
+               first_low = rtc1_read(ETIMELREG);
+               first_mid = rtc1_read(ETIMEMREG);
+               first_high = rtc1_read(ETIMEHREG);
+               second_low = rtc1_read(ETIMELREG);
+               second_mid = rtc1_read(ETIMEMREG);
+               second_high = rtc1_read(ETIMEHREG);
+       } while (first_low != second_low || first_mid != second_mid ||
+                first_high != second_high);
+
+       return (first_high << 17) | (first_mid << 1) | (first_low >> 15);
+}
+
+static inline void write_elapsed_second(unsigned long sec)
+{
+       spin_lock_irq(&rtc_lock);
+
+       rtc1_write(ETIMELREG, (uint16_t)(sec << 15));
+       rtc1_write(ETIMEMREG, (uint16_t)(sec >> 1));
+       rtc1_write(ETIMEHREG, (uint16_t)(sec >> 17));
+
+       spin_unlock_irq(&rtc_lock);
+}
+
+static void set_alarm(struct rtc_time *time)
+{
+       unsigned long alarm_sec;
+
+       alarm_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
+                          time->tm_hour, time->tm_min, time->tm_sec);
+
+       spin_lock_irq(&rtc_lock);
+
+       rtc1_write(ECMPLREG, (uint16_t)(alarm_sec << 15));
+       rtc1_write(ECMPMREG, (uint16_t)(alarm_sec >> 1));
+       rtc1_write(ECMPHREG, (uint16_t)(alarm_sec >> 17));
+
+       spin_unlock_irq(&rtc_lock);
+}
+
+static void read_alarm(struct rtc_time *time)
+{
+       unsigned long low, mid, high;
+
+       spin_lock_irq(&rtc_lock);
+
+       low = rtc1_read(ECMPLREG);
+       mid = rtc1_read(ECMPMREG);
+       high = rtc1_read(ECMPHREG);
+
+       spin_unlock_irq(&rtc_lock);
+
+       to_tm((high << 17) | (mid << 1) | (low >> 15), time);
+       time->tm_year -= 1900;
+}
+
+static void read_time(struct rtc_time *time)
+{
+       unsigned long epoch_sec, elapsed_sec;
+
+       epoch_sec = mktime(epoch, 1, 1, 0, 0, 0);
+       elapsed_sec = read_elapsed_second();
+
+       to_tm(epoch_sec + elapsed_sec, time);
+       time->tm_year -= 1900;
+}
+
+static void set_time(struct rtc_time *time)
+{
+       unsigned long epoch_sec, current_sec;
+
+       epoch_sec = mktime(epoch, 1, 1, 0, 0, 0);
+       current_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
+                            time->tm_hour, time->tm_min, time->tm_sec);
+
+       write_elapsed_second(current_sec - epoch_sec);
+}
+
+static ssize_t rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       unsigned long irq_data;
+       int retval = 0;
+
+       if (count != sizeof(unsigned int) && count != sizeof(unsigned long))
+               return -EINVAL;
+
+       add_wait_queue(&rtc_wait, &wait);
+
+       do {
+               __set_current_state(TASK_INTERRUPTIBLE);
+
+               spin_lock_irq(&rtc_lock);
+               irq_data = rtc_irq_data;
+               rtc_irq_data = 0;
+               spin_unlock_irq(&rtc_lock);
+
+               if (irq_data != 0)
+                       break;
+
+               if (file->f_flags & O_NONBLOCK) {
+                       retval = -EAGAIN;
+                       break;
+               }
+
+               if (signal_pending(current)) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+       } while (1);
+
+       if (retval == 0) {
+               if (count == sizeof(unsigned int)) {
+                       retval = put_user(irq_data, (unsigned int __user *)buf);
+                       if (retval == 0)
+                               retval = sizeof(unsigned int);
+               } else {
+                       retval = put_user(irq_data, (unsigned long __user *)buf);
+                       if (retval == 0)
+                               retval = sizeof(unsigned long);
+               }
+
+       }
+
+       __set_current_state(TASK_RUNNING);
+       remove_wait_queue(&rtc_wait, &wait);
+
+       return retval;
+}
+
+static unsigned int rtc_poll(struct file *file, struct poll_table_struct *table)
+{
+       poll_wait(file, &rtc_wait, table);
+
+       if (rtc_irq_data != 0)
+               return POLLIN | POLLRDNORM;
+
+       return 0;
+}
+
+static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, rtc_callfrom_t from)
+{
+       struct rtc_time time;
+       unsigned long count;
+
+       switch (cmd) {
+       case RTC_AIE_ON:
+               enable_irq(ELAPSEDTIME_IRQ);
+               break;
+       case RTC_AIE_OFF:
+               disable_irq(ELAPSEDTIME_IRQ);
+               break;
+       case RTC_PIE_ON:
+               enable_irq(RTCLONG1_IRQ);
+               break;
+       case RTC_PIE_OFF:
+               disable_irq(RTCLONG1_IRQ);
+               break;
+       case RTC_ALM_SET:
+               if (copy_from_user(&time, (struct rtc_time __user *)arg,
+                                  sizeof(struct rtc_time)))
+                       return -EFAULT;
+
+               set_alarm(&time);
+               break;
+       case RTC_ALM_READ:
+               memset(&time, 0, sizeof(struct rtc_time));
+               read_alarm(&time);
+               break;
+       case RTC_RD_TIME:
+               memset(&time, 0, sizeof(struct rtc_time));
+               read_time(&time);
+               if (copy_to_user((void __user *)arg, &time, sizeof(struct rtc_time)))
+                       return -EFAULT;
+               break;
+       case RTC_SET_TIME:
+               if (capable(CAP_SYS_TIME) == 0)
+                       return -EACCES;
+
+               if (copy_from_user(&time, (struct rtc_time __user *)arg,
+                                  sizeof(struct rtc_time)))
+                       return -EFAULT;
+
+               set_time(&time);
+               break;
+       case RTC_IRQP_READ:
+               return put_user(periodic_frequency, (unsigned long __user *)arg);
+               break;
+       case RTC_IRQP_SET:
+               if (arg > MAX_PERIODIC_RATE)
+                       return -EINVAL;
+
+               if (from == FUNCTION_RTC_IOCTL && arg > MAX_USER_PERIODIC_RATE &&
+                   capable(CAP_SYS_RESOURCE) == 0)
+                       return -EACCES;
+
+               periodic_frequency = arg;
+
+               count = RTC_FREQUENCY;
+               do_div(count, arg);
+
+               periodic_count = count;
+
+               spin_lock_irq(&rtc_lock);
+
+               rtc1_write(RTCL1LREG, count);
+               rtc1_write(RTCL1HREG, count >> 16);
+
+               spin_unlock_irq(&rtc_lock);
+               break;
+       case RTC_EPOCH_READ:
+               return put_user(epoch, (unsigned long __user *)arg);
+       case RTC_EPOCH_SET:
+               /* Doesn't support before 1900 */
+               if (arg < 1900)
+                       return -EINVAL;
+
+               if (capable(CAP_SYS_TIME) == 0)
+                       return -EACCES;
+
+               epoch = arg;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+                     unsigned long arg)
+{
+       return rtc_do_ioctl(cmd, arg, FUNCTION_RTC_IOCTL);
+}
+
+static int rtc_open(struct inode *inode, struct file *file)
+{
+       spin_lock_irq(&rtc_lock);
+
+       if (rtc_status == RTC_OPEN) {
+               spin_unlock_irq(&rtc_lock);
+               return -EBUSY;
+       }
+
+       rtc_status = RTC_OPEN;
+       rtc_irq_data = 0;
+
+       spin_unlock_irq(&rtc_lock);
+
+       return 0;
+}
+
+static int rtc_release(struct inode *inode, struct file *file)
+{
+       if (file->f_flags & FASYNC)
+               (void)fasync_helper(-1, file, 0, &rtc_async_queue);
+
+       spin_lock_irq(&rtc_lock);
+
+       rtc1_write(ECMPLREG, 0);
+       rtc1_write(ECMPMREG, 0);
+       rtc1_write(ECMPHREG, 0);
+       rtc1_write(RTCL1LREG, 0);
+       rtc1_write(RTCL1HREG, 0);
+
+       rtc_status = RTC_RELEASE;
+
+       spin_unlock_irq(&rtc_lock);
+
+       disable_irq(ELAPSEDTIME_IRQ);
+       disable_irq(RTCLONG1_IRQ);
+
+       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 irqreturn_t elapsedtime_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       spin_lock(&rtc_lock);
+       rtc2_write(RTCINTREG, ELAPSEDTIME_INT);
+
+       rtc_irq_data += 0x100;
+       rtc_irq_data &= ~0xff;
+       rtc_irq_data |= RTC_AF;
+       spin_unlock(&rtc_lock);
+
+       spin_lock(&rtc_lock);
+       if (rtc_callback)
+               rtc_callback->func(rtc_callback->private_data);
+       spin_unlock(&rtc_lock);
+
+       wake_up_interruptible(&rtc_wait);
+
+       kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t rtclong1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       unsigned long count = periodic_count;
+
+       spin_lock(&rtc_lock);
+       rtc2_write(RTCINTREG, RTCLONG1_INT);
+
+       rtc1_write(RTCL1LREG, count);
+       rtc1_write(RTCL1HREG, count >> 16);
+
+       rtc_irq_data += 0x100;
+       rtc_irq_data &= ~0xff;
+       rtc_irq_data |= RTC_PF;
+       spin_unlock(&rtc_lock);
+
+       spin_lock(&rtc_task_lock);
+       if (rtc_callback)
+               rtc_callback->func(rtc_callback->private_data);
+       spin_unlock(&rtc_task_lock);
+
+       wake_up_interruptible(&rtc_wait);
+
+       kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
+
+       return IRQ_HANDLED;
+}
+
+int rtc_register(rtc_task_t *task)
+{
+       if (task == NULL || task->func == NULL)
+               return -EINVAL;
+
+       spin_lock_irq(&rtc_lock);
+       if (rtc_status == RTC_OPEN) {
+               spin_unlock_irq(&rtc_lock);
+               return -EBUSY;
+       }
+
+       spin_lock(&rtc_task_lock);
+       if (rtc_callback != NULL) {
+               spin_unlock(&rtc_task_lock);
+               spin_unlock_irq(&rtc_task_lock);
+               return -EBUSY;
+       }
+
+       rtc_callback = task;
+       spin_unlock(&rtc_task_lock);
+
+       rtc_status = RTC_OPEN;
+
+       spin_unlock_irq(&rtc_lock);
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(rtc_register);
+
+int rtc_unregister(rtc_task_t *task)
+{
+       spin_lock_irq(&rtc_task_lock);
+       if (task == NULL || rtc_callback != task) {
+               spin_unlock_irq(&rtc_task_lock);
+               return -ENXIO;
+       }
+
+       spin_lock(&rtc_lock);
+
+       rtc1_write(ECMPLREG, 0);
+       rtc1_write(ECMPMREG, 0);
+       rtc1_write(ECMPHREG, 0);
+       rtc1_write(RTCL1LREG, 0);
+       rtc1_write(RTCL1HREG, 0);
+
+       rtc_status = RTC_RELEASE;
+
+       spin_unlock(&rtc_lock);
+
+       rtc_callback = NULL;
+
+       spin_unlock_irq(&rtc_task_lock);
+
+       disable_irq(ELAPSEDTIME_IRQ);
+       disable_irq(RTCLONG1_IRQ);
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(rtc_unregister);
+
+int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg)
+{
+       int retval = 0;
+
+       spin_lock_irq(&rtc_task_lock);
+
+       if (rtc_callback != task)
+               retval = -ENXIO;
+       else
+               rtc_do_ioctl(cmd, arg, FUNCTION_RTC_CONTROL);
+
+       spin_unlock_irq(&rtc_task_lock);
+
+       return retval;
+}
+
+EXPORT_SYMBOL_GPL(rtc_control);
+
+static struct miscdevice rtc_miscdevice = {
+       .minor  = RTC_MINOR,
+       .name   = rtc_name,
+       .fops   = &rtc_fops,
+};
+
+static int rtc_probe(struct device *dev)
+{
+       struct platform_device *pdev;
+       unsigned int irq;
+       int retval;
+
+       pdev = to_platform_device(dev);
+       if (pdev->num_resources != 2)
+               return -EBUSY;
+
+       rtc1_base = ioremap(pdev->resource[0].start, RTC1_SIZE);
+       if (rtc1_base == NULL)
+               return -EBUSY;
+
+       rtc2_base = ioremap(pdev->resource[1].start, RTC2_SIZE);
+       if (rtc2_base == NULL) {
+               iounmap(rtc1_base);
+               rtc1_base = NULL;
+               return -EBUSY;
+       }
+
+       retval = misc_register(&rtc_miscdevice);
+       if (retval < 0) {
+               iounmap(rtc1_base);
+               iounmap(rtc2_base);
+               rtc1_base = NULL;
+               rtc2_base = NULL;
+               return retval;
+       }
+
+       spin_lock_irq(&rtc_lock);
+
+       rtc1_write(ECMPLREG, 0);
+       rtc1_write(ECMPMREG, 0);
+       rtc1_write(ECMPHREG, 0);
+       rtc1_write(RTCL1LREG, 0);
+       rtc1_write(RTCL1HREG, 0);
+
+       rtc_status = RTC_RELEASE;
+       rtc_irq_data = 0;
+
+       spin_unlock_irq(&rtc_lock);
+
+       init_waitqueue_head(&rtc_wait);
+
+       irq = ELAPSEDTIME_IRQ;
+       retval = request_irq(irq, elapsedtime_interrupt, SA_INTERRUPT,
+                            "elapsed_time", NULL);
+       if (retval == 0) {
+               irq = RTCLONG1_IRQ;
+               retval = request_irq(irq, rtclong1_interrupt, SA_INTERRUPT,
+                                    "rtclong1", NULL);
+       }
+
+       if (retval < 0) {
+               printk(KERN_ERR "rtc: IRQ%d is busy\n", irq);
+               if (irq == RTCLONG1_IRQ)
+                       free_irq(ELAPSEDTIME_IRQ, NULL);
+               iounmap(rtc1_base);
+               iounmap(rtc2_base);
+               rtc1_base = NULL;
+               rtc2_base = NULL;
+               return retval;
+       }
+
+       disable_irq(ELAPSEDTIME_IRQ);
+       disable_irq(RTCLONG1_IRQ);
+
+       spin_lock_init(&rtc_task_lock);
+
+       printk(KERN_INFO "rtc: Real Time Clock of NEC VR4100 series\n");
+
+       return 0;
+}
+
+static int rtc_remove(struct device *dev)
+{
+       int retval;
+
+       retval = misc_deregister(&rtc_miscdevice);
+       if (retval < 0)
+               return retval;
+
+       free_irq(ELAPSEDTIME_IRQ, NULL);
+       free_irq(RTCLONG1_IRQ, NULL);
+       if (rtc1_base != NULL)
+               iounmap(rtc1_base);
+       if (rtc2_base != NULL)
+               iounmap(rtc2_base);
+
+       return 0;
+}
+
+static struct platform_device *rtc_platform_device;
+
+static struct device_driver rtc_device_driver = {
+       .name           = rtc_name,
+       .bus            = &platform_bus_type,
+       .probe          = rtc_probe,
+       .remove         = rtc_remove,
+};
+
+static int __devinit vr41xx_rtc_init(void)
+{
+       int retval;
+
+       switch (current_cpu_data.cputype) {
+       case CPU_VR4111:
+       case CPU_VR4121:
+               rtc_resource[0].start = RTC1_TYPE1_START;
+               rtc_resource[0].end = RTC1_TYPE1_END;
+               rtc_resource[1].start = RTC2_TYPE1_START;
+               rtc_resource[1].end = RTC2_TYPE1_END;
+               break;
+       case CPU_VR4122:
+       case CPU_VR4131:
+       case CPU_VR4133:
+               rtc_resource[0].start = RTC1_TYPE2_START;
+               rtc_resource[0].end = RTC1_TYPE2_END;
+               rtc_resource[1].start = RTC2_TYPE2_START;
+               rtc_resource[1].end = RTC2_TYPE2_END;
+               break;
+       default:
+               return -ENODEV;
+               break;
+       }
+
+       rtc_platform_device = platform_device_register_simple("RTC", -1, rtc_resource, RTC_NUM_RESOURCES);
+       if (IS_ERR(rtc_platform_device))
+               return PTR_ERR(rtc_platform_device);
+
+       retval = driver_register(&rtc_device_driver);
+       if (retval < 0)
+               platform_device_unregister(rtc_platform_device);
+
+       return retval;
+}
+
+static void __devexit vr41xx_rtc_exit(void)
+{
+       driver_unregister(&rtc_device_driver);
+
+       platform_device_unregister(rtc_platform_device);
+}
+
+module_init(vr41xx_rtc_init);
+module_exit(vr41xx_rtc_exit);
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
new file mode 100644 (file)
index 0000000..e1df376
--- /dev/null
@@ -0,0 +1,586 @@
+/*
+ *  drivers/cpufreq/cpufreq_conservative.c
+ *
+ *  Copyright (C)  2001 Russell King
+ *            (C)  2003 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
+ *                      Jun Nakajima <jun.nakajima@intel.com>
+ *            (C)  2004 Alexander Clouter <alex-kernel@digriz.org.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/smp.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ctype.h>
+#include <linux/cpufreq.h>
+#include <linux/sysctl.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/sysfs.h>
+#include <linux/sched.h>
+#include <linux/kmod.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/kernel_stat.h>
+#include <linux/percpu.h>
+
+/*
+ * dbs is used in this file as a shortform for demandbased switching
+ * It helps to keep variable names smaller, simpler
+ */
+
+#define DEF_FREQUENCY_UP_THRESHOLD             (80)
+#define MIN_FREQUENCY_UP_THRESHOLD             (0)
+#define MAX_FREQUENCY_UP_THRESHOLD             (100)
+
+#define DEF_FREQUENCY_DOWN_THRESHOLD           (20)
+#define MIN_FREQUENCY_DOWN_THRESHOLD           (0)
+#define MAX_FREQUENCY_DOWN_THRESHOLD           (100)
+
+/* 
+ * The polling frequency of this governor depends on the capability of 
+ * the processor. Default polling frequency is 1000 times the transition
+ * latency of the processor. The governor will work on any processor with 
+ * transition latency <= 10mS, using appropriate sampling 
+ * rate.
+ * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL)
+ * this governor will not work.
+ * All times here are in uS.
+ */
+static unsigned int                            def_sampling_rate;
+#define MIN_SAMPLING_RATE                      (def_sampling_rate / 2)
+#define MAX_SAMPLING_RATE                      (500 * def_sampling_rate)
+#define DEF_SAMPLING_RATE_LATENCY_MULTIPLIER   (100000)
+#define DEF_SAMPLING_DOWN_FACTOR               (5)
+#define TRANSITION_LATENCY_LIMIT               (10 * 1000)
+
+static void do_dbs_timer(void *data);
+
+struct cpu_dbs_info_s {
+       struct cpufreq_policy   *cur_policy;
+       unsigned int            prev_cpu_idle_up;
+       unsigned int            prev_cpu_idle_down;
+       unsigned int            enable;
+};
+static DEFINE_PER_CPU(struct cpu_dbs_info_s, cpu_dbs_info);
+
+static unsigned int dbs_enable;        /* number of CPUs using this policy */
+
+static DECLARE_MUTEX   (dbs_sem);
+static DECLARE_WORK    (dbs_work, do_dbs_timer, NULL);
+
+struct dbs_tuners {
+       unsigned int            sampling_rate;
+       unsigned int            sampling_down_factor;
+       unsigned int            up_threshold;
+       unsigned int            down_threshold;
+       unsigned int            ignore_nice;
+       unsigned int            freq_step;
+};
+
+static struct dbs_tuners dbs_tuners_ins = {
+       .up_threshold           = DEF_FREQUENCY_UP_THRESHOLD,
+       .down_threshold         = DEF_FREQUENCY_DOWN_THRESHOLD,
+       .sampling_down_factor   = DEF_SAMPLING_DOWN_FACTOR,
+};
+
+static inline unsigned int get_cpu_idle_time(unsigned int cpu)
+{
+       return  kstat_cpu(cpu).cpustat.idle +
+               kstat_cpu(cpu).cpustat.iowait +
+               ( !dbs_tuners_ins.ignore_nice ? 
+                 kstat_cpu(cpu).cpustat.nice :
+                 0);
+}
+
+/************************** sysfs interface ************************/
+static ssize_t show_sampling_rate_max(struct cpufreq_policy *policy, char *buf)
+{
+       return sprintf (buf, "%u\n", MAX_SAMPLING_RATE);
+}
+
+static ssize_t show_sampling_rate_min(struct cpufreq_policy *policy, char *buf)
+{
+       return sprintf (buf, "%u\n", MIN_SAMPLING_RATE);
+}
+
+#define define_one_ro(_name)                                   \
+static struct freq_attr _name =                                \
+__ATTR(_name, 0444, show_##_name, NULL)
+
+define_one_ro(sampling_rate_max);
+define_one_ro(sampling_rate_min);
+
+/* cpufreq_conservative Governor Tunables */
+#define show_one(file_name, object)                                    \
+static ssize_t show_##file_name                                                \
+(struct cpufreq_policy *unused, char *buf)                             \
+{                                                                      \
+       return sprintf(buf, "%u\n", dbs_tuners_ins.object);             \
+}
+show_one(sampling_rate, sampling_rate);
+show_one(sampling_down_factor, sampling_down_factor);
+show_one(up_threshold, up_threshold);
+show_one(down_threshold, down_threshold);
+show_one(ignore_nice, ignore_nice);
+show_one(freq_step, freq_step);
+
+static ssize_t store_sampling_down_factor(struct cpufreq_policy *unused, 
+               const char *buf, size_t count)
+{
+       unsigned int input;
+       int ret;
+       ret = sscanf (buf, "%u", &input);
+       if (ret != 1 )
+               return -EINVAL;
+
+       down(&dbs_sem);
+       dbs_tuners_ins.sampling_down_factor = input;
+       up(&dbs_sem);
+
+       return count;
+}
+
+static ssize_t store_sampling_rate(struct cpufreq_policy *unused, 
+               const char *buf, size_t count)
+{
+       unsigned int input;
+       int ret;
+       ret = sscanf (buf, "%u", &input);
+
+       down(&dbs_sem);
+       if (ret != 1 || input > MAX_SAMPLING_RATE || input < MIN_SAMPLING_RATE) {
+               up(&dbs_sem);
+               return -EINVAL;
+       }
+
+       dbs_tuners_ins.sampling_rate = input;
+       up(&dbs_sem);
+
+       return count;
+}
+
+static ssize_t store_up_threshold(struct cpufreq_policy *unused, 
+               const char *buf, size_t count)
+{
+       unsigned int input;
+       int ret;
+       ret = sscanf (buf, "%u", &input);
+
+       down(&dbs_sem);
+       if (ret != 1 || input > MAX_FREQUENCY_UP_THRESHOLD || 
+                       input < MIN_FREQUENCY_UP_THRESHOLD ||
+                       input <= dbs_tuners_ins.down_threshold) {
+               up(&dbs_sem);
+               return -EINVAL;
+       }
+
+       dbs_tuners_ins.up_threshold = input;
+       up(&dbs_sem);
+
+       return count;
+}
+
+static ssize_t store_down_threshold(struct cpufreq_policy *unused, 
+               const char *buf, size_t count)
+{
+       unsigned int input;
+       int ret;
+       ret = sscanf (buf, "%u", &input);
+
+       down(&dbs_sem);
+       if (ret != 1 || input > MAX_FREQUENCY_DOWN_THRESHOLD || 
+                       input < MIN_FREQUENCY_DOWN_THRESHOLD ||
+                       input >= dbs_tuners_ins.up_threshold) {
+               up(&dbs_sem);
+               return -EINVAL;
+       }
+
+       dbs_tuners_ins.down_threshold = input;
+       up(&dbs_sem);
+
+       return count;
+}
+
+static ssize_t store_ignore_nice(struct cpufreq_policy *policy,
+               const char *buf, size_t count)
+{
+       unsigned int input;
+       int ret;
+
+       unsigned int j;
+       
+       ret = sscanf (buf, "%u", &input);
+       if ( ret != 1 )
+               return -EINVAL;
+
+       if ( input > 1 )
+               input = 1;
+       
+       down(&dbs_sem);
+       if ( input == dbs_tuners_ins.ignore_nice ) { /* nothing to do */
+               up(&dbs_sem);
+               return count;
+       }
+       dbs_tuners_ins.ignore_nice = input;
+
+       /* we need to re-evaluate prev_cpu_idle_up and prev_cpu_idle_down */
+       for_each_online_cpu(j) {
+               struct cpu_dbs_info_s *j_dbs_info;
+               j_dbs_info = &per_cpu(cpu_dbs_info, j);
+               j_dbs_info->prev_cpu_idle_up = get_cpu_idle_time(j);
+               j_dbs_info->prev_cpu_idle_down = j_dbs_info->prev_cpu_idle_up;
+       }
+       up(&dbs_sem);
+
+       return count;
+}
+
+static ssize_t store_freq_step(struct cpufreq_policy *policy,
+               const char *buf, size_t count)
+{
+       unsigned int input;
+       int ret;
+
+       ret = sscanf (buf, "%u", &input);
+
+       if ( ret != 1 )
+               return -EINVAL;
+
+       if ( input > 100 )
+               input = 100;
+       
+       /* no need to test here if freq_step is zero as the user might actually
+        * want this, they would be crazy though :) */
+       down(&dbs_sem);
+       dbs_tuners_ins.freq_step = input;
+       up(&dbs_sem);
+
+       return count;
+}
+
+#define define_one_rw(_name) \
+static struct freq_attr _name = \
+__ATTR(_name, 0644, show_##_name, store_##_name)
+
+define_one_rw(sampling_rate);
+define_one_rw(sampling_down_factor);
+define_one_rw(up_threshold);
+define_one_rw(down_threshold);
+define_one_rw(ignore_nice);
+define_one_rw(freq_step);
+
+static struct attribute * dbs_attributes[] = {
+       &sampling_rate_max.attr,
+       &sampling_rate_min.attr,
+       &sampling_rate.attr,
+       &sampling_down_factor.attr,
+       &up_threshold.attr,
+       &down_threshold.attr,
+       &ignore_nice.attr,
+       &freq_step.attr,
+       NULL
+};
+
+static struct attribute_group dbs_attr_group = {
+       .attrs = dbs_attributes,
+       .name = "conservative",
+};
+
+/************************** sysfs end ************************/
+
+static void dbs_check_cpu(int cpu)
+{
+       unsigned int idle_ticks, up_idle_ticks, down_idle_ticks;
+       unsigned int freq_step;
+       unsigned int freq_down_sampling_rate;
+       static int down_skip[NR_CPUS];
+       static int requested_freq[NR_CPUS];
+       static unsigned short init_flag = 0;
+       struct cpu_dbs_info_s *this_dbs_info;
+       struct cpu_dbs_info_s *dbs_info;
+
+       struct cpufreq_policy *policy;
+       unsigned int j;
+
+       this_dbs_info = &per_cpu(cpu_dbs_info, cpu);
+       if (!this_dbs_info->enable)
+               return;
+
+       policy = this_dbs_info->cur_policy;
+
+       if ( init_flag == 0 ) {
+               for ( /* NULL */; init_flag < NR_CPUS; init_flag++ ) {
+                       dbs_info = &per_cpu(cpu_dbs_info, init_flag);
+                       requested_freq[cpu] = dbs_info->cur_policy->cur;
+               }
+               init_flag = 1;
+       }
+       
+       /* 
+        * The default safe range is 20% to 80% 
+        * Every sampling_rate, we check
+        *      - If current idle time is less than 20%, then we try to 
+        *        increase frequency
+        * Every sampling_rate*sampling_down_factor, we check
+        *      - If current idle time is more than 80%, then we try to
+        *        decrease frequency
+        *
+        * Any frequency increase takes it to the maximum frequency. 
+        * Frequency reduction happens at minimum steps of 
+        * 5% (default) of max_frequency 
+        */
+
+       /* Check for frequency increase */
+
+       idle_ticks = UINT_MAX;
+       for_each_cpu_mask(j, policy->cpus) {
+               unsigned int tmp_idle_ticks, total_idle_ticks;
+               struct cpu_dbs_info_s *j_dbs_info;
+
+               j_dbs_info = &per_cpu(cpu_dbs_info, j);
+               /* Check for frequency increase */
+               total_idle_ticks = get_cpu_idle_time(j);
+               tmp_idle_ticks = total_idle_ticks -
+                       j_dbs_info->prev_cpu_idle_up;
+               j_dbs_info->prev_cpu_idle_up = total_idle_ticks;
+
+               if (tmp_idle_ticks < idle_ticks)
+                       idle_ticks = tmp_idle_ticks;
+       }
+
+       /* Scale idle ticks by 100 and compare with up and down ticks */
+       idle_ticks *= 100;
+       up_idle_ticks = (100 - dbs_tuners_ins.up_threshold) *
+               usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
+
+       if (idle_ticks < up_idle_ticks) {
+               down_skip[cpu] = 0;
+               for_each_cpu_mask(j, policy->cpus) {
+                       struct cpu_dbs_info_s *j_dbs_info;
+
+                       j_dbs_info = &per_cpu(cpu_dbs_info, j);
+                       j_dbs_info->prev_cpu_idle_down = 
+                                       j_dbs_info->prev_cpu_idle_up;
+               }
+               /* if we are already at full speed then break out early */
+               if (requested_freq[cpu] == policy->max)
+                       return;
+               
+               freq_step = (dbs_tuners_ins.freq_step * policy->max) / 100;
+
+               /* max freq cannot be less than 100. But who knows.... */
+               if (unlikely(freq_step == 0))
+                       freq_step = 5;
+               
+               requested_freq[cpu] += freq_step;
+               if (requested_freq[cpu] > policy->max)
+                       requested_freq[cpu] = policy->max;
+
+               __cpufreq_driver_target(policy, requested_freq[cpu], 
+                       CPUFREQ_RELATION_H);
+               return;
+       }
+
+       /* Check for frequency decrease */
+       down_skip[cpu]++;
+       if (down_skip[cpu] < dbs_tuners_ins.sampling_down_factor)
+               return;
+
+       idle_ticks = UINT_MAX;
+       for_each_cpu_mask(j, policy->cpus) {
+               unsigned int tmp_idle_ticks, total_idle_ticks;
+               struct cpu_dbs_info_s *j_dbs_info;
+
+               j_dbs_info = &per_cpu(cpu_dbs_info, j);
+               total_idle_ticks = j_dbs_info->prev_cpu_idle_up;
+               tmp_idle_ticks = total_idle_ticks -
+                       j_dbs_info->prev_cpu_idle_down;
+               j_dbs_info->prev_cpu_idle_down = total_idle_ticks;
+
+               if (tmp_idle_ticks < idle_ticks)
+                       idle_ticks = tmp_idle_ticks;
+       }
+
+       /* Scale idle ticks by 100 and compare with up and down ticks */
+       idle_ticks *= 100;
+       down_skip[cpu] = 0;
+
+       freq_down_sampling_rate = dbs_tuners_ins.sampling_rate *
+               dbs_tuners_ins.sampling_down_factor;
+       down_idle_ticks = (100 - dbs_tuners_ins.down_threshold) *
+                       usecs_to_jiffies(freq_down_sampling_rate);
+
+       if (idle_ticks > down_idle_ticks) {
+               /* if we are already at the lowest speed then break out early
+                * or if we 'cannot' reduce the speed as the user might want
+                * freq_step to be zero */
+               if (requested_freq[cpu] == policy->min
+                               || dbs_tuners_ins.freq_step == 0)
+                       return;
+
+               freq_step = (dbs_tuners_ins.freq_step * policy->max) / 100;
+
+               /* max freq cannot be less than 100. But who knows.... */
+               if (unlikely(freq_step == 0))
+                       freq_step = 5;
+
+               requested_freq[cpu] -= freq_step;
+               if (requested_freq[cpu] < policy->min)
+                       requested_freq[cpu] = policy->min;
+
+               __cpufreq_driver_target(policy,
+                       requested_freq[cpu],
+                       CPUFREQ_RELATION_H);
+               return;
+       }
+}
+
+static void do_dbs_timer(void *data)
+{ 
+       int i;
+       down(&dbs_sem);
+       for_each_online_cpu(i)
+               dbs_check_cpu(i);
+       schedule_delayed_work(&dbs_work, 
+                       usecs_to_jiffies(dbs_tuners_ins.sampling_rate));
+       up(&dbs_sem);
+} 
+
+static inline void dbs_timer_init(void)
+{
+       INIT_WORK(&dbs_work, do_dbs_timer, NULL);
+       schedule_delayed_work(&dbs_work,
+                       usecs_to_jiffies(dbs_tuners_ins.sampling_rate));
+       return;
+}
+
+static inline void dbs_timer_exit(void)
+{
+       cancel_delayed_work(&dbs_work);
+       return;
+}
+
+static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
+                                  unsigned int event)
+{
+       unsigned int cpu = policy->cpu;
+       struct cpu_dbs_info_s *this_dbs_info;
+       unsigned int j;
+
+       this_dbs_info = &per_cpu(cpu_dbs_info, cpu);
+
+       switch (event) {
+       case CPUFREQ_GOV_START:
+               if ((!cpu_online(cpu)) || 
+                   (!policy->cur))
+                       return -EINVAL;
+
+               if (policy->cpuinfo.transition_latency >
+                               (TRANSITION_LATENCY_LIMIT * 1000))
+                       return -EINVAL;
+               if (this_dbs_info->enable) /* Already enabled */
+                       break;
+                
+               down(&dbs_sem);
+               for_each_cpu_mask(j, policy->cpus) {
+                       struct cpu_dbs_info_s *j_dbs_info;
+                       j_dbs_info = &per_cpu(cpu_dbs_info, j);
+                       j_dbs_info->cur_policy = policy;
+               
+                       j_dbs_info->prev_cpu_idle_up = get_cpu_idle_time(j);
+                       j_dbs_info->prev_cpu_idle_down
+                               = j_dbs_info->prev_cpu_idle_up;
+               }
+               this_dbs_info->enable = 1;
+               sysfs_create_group(&policy->kobj, &dbs_attr_group);
+               dbs_enable++;
+               /*
+                * Start the timerschedule work, when this governor
+                * is used for first time
+                */
+               if (dbs_enable == 1) {
+                       unsigned int latency;
+                       /* policy latency is in nS. Convert it to uS first */
+
+                       latency = policy->cpuinfo.transition_latency;
+                       if (latency < 1000)
+                               latency = 1000;
+
+                       def_sampling_rate = (latency / 1000) *
+                                       DEF_SAMPLING_RATE_LATENCY_MULTIPLIER;
+                       dbs_tuners_ins.sampling_rate = def_sampling_rate;
+                       dbs_tuners_ins.ignore_nice = 0;
+                       dbs_tuners_ins.freq_step = 5;
+
+                       dbs_timer_init();
+               }
+               
+               up(&dbs_sem);
+               break;
+
+       case CPUFREQ_GOV_STOP:
+               down(&dbs_sem);
+               this_dbs_info->enable = 0;
+               sysfs_remove_group(&policy->kobj, &dbs_attr_group);
+               dbs_enable--;
+               /*
+                * Stop the timerschedule work, when this governor
+                * is used for first time
+                */
+               if (dbs_enable == 0) 
+                       dbs_timer_exit();
+               
+               up(&dbs_sem);
+
+               break;
+
+       case CPUFREQ_GOV_LIMITS:
+               down(&dbs_sem);
+               if (policy->max < this_dbs_info->cur_policy->cur)
+                       __cpufreq_driver_target(
+                                       this_dbs_info->cur_policy,
+                                       policy->max, CPUFREQ_RELATION_H);
+               else if (policy->min > this_dbs_info->cur_policy->cur)
+                       __cpufreq_driver_target(
+                                       this_dbs_info->cur_policy,
+                                       policy->min, CPUFREQ_RELATION_L);
+               up(&dbs_sem);
+               break;
+       }
+       return 0;
+}
+
+static struct cpufreq_governor cpufreq_gov_dbs = {
+       .name           = "conservative",
+       .governor       = cpufreq_governor_dbs,
+       .owner          = THIS_MODULE,
+};
+
+static int __init cpufreq_gov_dbs_init(void)
+{
+       return cpufreq_register_governor(&cpufreq_gov_dbs);
+}
+
+static void __exit cpufreq_gov_dbs_exit(void)
+{
+       /* Make sure that the scheduled work is indeed not running */
+       flush_scheduled_work();
+
+       cpufreq_unregister_governor(&cpufreq_gov_dbs);
+}
+
+
+MODULE_AUTHOR ("Alexander Clouter <alex-kernel@digriz.org.uk>");
+MODULE_DESCRIPTION ("'cpufreq_conservative' - A dynamic cpufreq governor for "
+               "Low Latency Frequency Transition capable processors "
+               "optimised for use in a battery environment");
+MODULE_LICENSE ("GPL");
+
+module_init(cpufreq_gov_dbs_init);
+module_exit(cpufreq_gov_dbs_exit);
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
new file mode 100644 (file)
index 0000000..5b85278
--- /dev/null
@@ -0,0 +1,598 @@
+/*
+ * drivers/i2c/busses/i2c-mv64xxx.c
+ * 
+ * Driver for the i2c controller on the Marvell line of host bridges for MIPS
+ * and PPC (e.g, gt642[46]0, mv643[46]0, mv644[46]0).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (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/module.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mv643xx.h>
+#include <asm/io.h>
+
+/* Register defines */
+#define        MV64XXX_I2C_REG_SLAVE_ADDR                      0x00
+#define        MV64XXX_I2C_REG_DATA                            0x04
+#define        MV64XXX_I2C_REG_CONTROL                         0x08
+#define        MV64XXX_I2C_REG_STATUS                          0x0c
+#define        MV64XXX_I2C_REG_BAUD                            0x0c
+#define        MV64XXX_I2C_REG_EXT_SLAVE_ADDR                  0x10
+#define        MV64XXX_I2C_REG_SOFT_RESET                      0x1c
+
+#define        MV64XXX_I2C_REG_CONTROL_ACK                     0x00000004
+#define        MV64XXX_I2C_REG_CONTROL_IFLG                    0x00000008
+#define        MV64XXX_I2C_REG_CONTROL_STOP                    0x00000010
+#define        MV64XXX_I2C_REG_CONTROL_START                   0x00000020
+#define        MV64XXX_I2C_REG_CONTROL_TWSIEN                  0x00000040
+#define        MV64XXX_I2C_REG_CONTROL_INTEN                   0x00000080
+
+/* Ctlr status values */
+#define        MV64XXX_I2C_STATUS_BUS_ERR                      0x00
+#define        MV64XXX_I2C_STATUS_MAST_START                   0x08
+#define        MV64XXX_I2C_STATUS_MAST_REPEAT_START            0x10
+#define        MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK             0x18
+#define        MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK          0x20
+#define        MV64XXX_I2C_STATUS_MAST_WR_ACK                  0x28
+#define        MV64XXX_I2C_STATUS_MAST_WR_NO_ACK               0x30
+#define        MV64XXX_I2C_STATUS_MAST_LOST_ARB                0x38
+#define        MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK             0x40
+#define        MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK          0x48
+#define        MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK             0x50
+#define        MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK          0x58
+#define        MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK           0xd0
+#define        MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK        0xd8
+#define        MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK           0xe0
+#define        MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK        0xe8
+#define        MV64XXX_I2C_STATUS_NO_STATUS                    0xf8
+
+/* Driver states */
+enum {
+       MV64XXX_I2C_STATE_INVALID,
+       MV64XXX_I2C_STATE_IDLE,
+       MV64XXX_I2C_STATE_WAITING_FOR_START_COND,
+       MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK,
+       MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK,
+       MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK,
+       MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA,
+       MV64XXX_I2C_STATE_ABORTING,
+};
+
+/* Driver actions */
+enum {
+       MV64XXX_I2C_ACTION_INVALID,
+       MV64XXX_I2C_ACTION_CONTINUE,
+       MV64XXX_I2C_ACTION_SEND_START,
+       MV64XXX_I2C_ACTION_SEND_ADDR_1,
+       MV64XXX_I2C_ACTION_SEND_ADDR_2,
+       MV64XXX_I2C_ACTION_SEND_DATA,
+       MV64XXX_I2C_ACTION_RCV_DATA,
+       MV64XXX_I2C_ACTION_RCV_DATA_STOP,
+       MV64XXX_I2C_ACTION_SEND_STOP,
+};
+
+struct mv64xxx_i2c_data {
+       int                     irq;
+       u32                     state;
+       u32                     action;
+       u32                     cntl_bits;
+       void __iomem            *reg_base;
+       u32                     reg_base_p;
+       u32                     addr1;
+       u32                     addr2;
+       u32                     bytes_left;
+       u32                     byte_posn;
+       u32                     block;
+       int                     rc;
+       u32                     freq_m;
+       u32                     freq_n;
+       wait_queue_head_t       waitq;
+       spinlock_t              lock;
+       struct i2c_msg          *msg;
+       struct i2c_adapter      adapter;
+};
+
+/*
+ *****************************************************************************
+ *
+ *     Finite State Machine & Interrupt Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status)
+{
+       /*
+        * If state is idle, then this is likely the remnants of an old
+        * operation that driver has given up on or the user has killed.
+        * If so, issue the stop condition and go to idle.
+        */
+       if (drv_data->state == MV64XXX_I2C_STATE_IDLE) {
+               drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+               return;
+       }
+
+       if (drv_data->state == MV64XXX_I2C_STATE_ABORTING) {
+               drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+               drv_data->state = MV64XXX_I2C_STATE_IDLE;
+               return;
+       }
+
+       /* The status from the ctlr [mostly] tells us what to do next */
+       switch (status) {
+       /* Start condition interrupt */
+       case MV64XXX_I2C_STATUS_MAST_START: /* 0x08 */
+       case MV64XXX_I2C_STATUS_MAST_REPEAT_START: /* 0x10 */
+               drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1;
+               drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK;
+               break;
+
+       /* Performing a write */
+       case MV64XXX_I2C_STATUS_MAST_WR_ADDR_ACK: /* 0x18 */
+               if (drv_data->msg->flags & I2C_M_TEN) {
+                       drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+                       drv_data->state =
+                               MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+                       break;
+               }
+               /* FALLTHRU */
+       case MV64XXX_I2C_STATUS_MAST_WR_ADDR_2_ACK: /* 0xd0 */
+       case MV64XXX_I2C_STATUS_MAST_WR_ACK: /* 0x28 */
+               if (drv_data->bytes_left > 0) {
+                       drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+                       drv_data->state =
+                               MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+                       drv_data->bytes_left--;
+               } else {
+                       drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+                       drv_data->state = MV64XXX_I2C_STATE_IDLE;
+               }
+               break;
+
+       /* Performing a read */
+       case MV64XXX_I2C_STATUS_MAST_RD_ADDR_ACK: /* 40 */
+               if (drv_data->msg->flags & I2C_M_TEN) {
+                       drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_2;
+                       drv_data->state =
+                               MV64XXX_I2C_STATE_WAITING_FOR_ADDR_2_ACK;
+                       break;
+               }
+               /* FALLTHRU */
+       case MV64XXX_I2C_STATUS_MAST_RD_ADDR_2_ACK: /* 0xe0 */
+               if (drv_data->bytes_left == 0) {
+                       drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+                       drv_data->state = MV64XXX_I2C_STATE_IDLE;
+                       break;
+               }
+               /* FALLTHRU */
+       case MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK: /* 0x50 */
+               if (status != MV64XXX_I2C_STATUS_MAST_RD_DATA_ACK)
+                       drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+               else {
+                       drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA;
+                       drv_data->bytes_left--;
+               }
+               drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+
+               if (drv_data->bytes_left == 1)
+                       drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_ACK;
+               break;
+
+       case MV64XXX_I2C_STATUS_MAST_RD_DATA_NO_ACK: /* 0x58 */
+               drv_data->action = MV64XXX_I2C_ACTION_RCV_DATA_STOP;
+               drv_data->state = MV64XXX_I2C_STATE_IDLE;
+               break;
+
+       case MV64XXX_I2C_STATUS_MAST_WR_ADDR_NO_ACK: /* 0x20 */
+       case MV64XXX_I2C_STATUS_MAST_WR_NO_ACK: /* 30 */
+       case MV64XXX_I2C_STATUS_MAST_RD_ADDR_NO_ACK: /* 48 */
+               /* Doesn't seem to be a device at other end */
+               drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+               drv_data->state = MV64XXX_I2C_STATE_IDLE;
+               drv_data->rc = -ENODEV;
+               break;
+
+       default:
+               dev_err(&drv_data->adapter.dev,
+                       "mv64xxx_i2c_fsm: Ctlr Error -- state: 0x%x, "
+                       "status: 0x%x, addr: 0x%x, flags: 0x%x\n",
+                        drv_data->state, status, drv_data->msg->addr,
+                        drv_data->msg->flags);
+               drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP;
+               drv_data->state = MV64XXX_I2C_STATE_IDLE;
+               drv_data->rc = -EIO;
+       }
+}
+
+static void
+mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data)
+{
+       switch(drv_data->action) {
+       case MV64XXX_I2C_ACTION_CONTINUE:
+               writel(drv_data->cntl_bits,
+                       drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+               break;
+
+       case MV64XXX_I2C_ACTION_SEND_START:
+               writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_START,
+                       drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+               break;
+
+       case MV64XXX_I2C_ACTION_SEND_ADDR_1:
+               writel(drv_data->addr1,
+                       drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+               writel(drv_data->cntl_bits,
+                       drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+               break;
+
+       case MV64XXX_I2C_ACTION_SEND_ADDR_2:
+               writel(drv_data->addr2,
+                       drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+               writel(drv_data->cntl_bits,
+                       drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+               break;
+
+       case MV64XXX_I2C_ACTION_SEND_DATA:
+               writel(drv_data->msg->buf[drv_data->byte_posn++],
+                       drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+               writel(drv_data->cntl_bits,
+                       drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+               break;
+
+       case MV64XXX_I2C_ACTION_RCV_DATA:
+               drv_data->msg->buf[drv_data->byte_posn++] =
+                       readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+               writel(drv_data->cntl_bits,
+                       drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+               break;
+
+       case MV64XXX_I2C_ACTION_RCV_DATA_STOP:
+               drv_data->msg->buf[drv_data->byte_posn++] =
+                       readl(drv_data->reg_base + MV64XXX_I2C_REG_DATA);
+               drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+               writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+                       drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+               drv_data->block = 0;
+               wake_up_interruptible(&drv_data->waitq);
+               break;
+
+       case MV64XXX_I2C_ACTION_INVALID:
+       default:
+               dev_err(&drv_data->adapter.dev,
+                       "mv64xxx_i2c_do_action: Invalid action: %d\n",
+                       drv_data->action);
+               drv_data->rc = -EIO;
+               /* FALLTHRU */
+       case MV64XXX_I2C_ACTION_SEND_STOP:
+               drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN;
+               writel(drv_data->cntl_bits | MV64XXX_I2C_REG_CONTROL_STOP,
+                       drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+               drv_data->block = 0;
+               wake_up_interruptible(&drv_data->waitq);
+               break;
+       }
+}
+
+static int
+mv64xxx_i2c_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct mv64xxx_i2c_data *drv_data = dev_id;
+       unsigned long   flags;
+       u32             status;
+       int             rc = IRQ_NONE;
+
+       spin_lock_irqsave(&drv_data->lock, flags);
+       while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) &
+                                               MV64XXX_I2C_REG_CONTROL_IFLG) {
+               status = readl(drv_data->reg_base + MV64XXX_I2C_REG_STATUS);
+               mv64xxx_i2c_fsm(drv_data, status);
+               mv64xxx_i2c_do_action(drv_data);
+               rc = IRQ_HANDLED;
+       }
+       spin_unlock_irqrestore(&drv_data->lock, flags);
+
+       return rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *     I2C Msg Execution Routines
+ *
+ *****************************************************************************
+ */
+static void
+mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data,
+       struct i2c_msg *msg)
+{
+       u32     dir = 0;
+
+       drv_data->msg = msg;
+       drv_data->byte_posn = 0;
+       drv_data->bytes_left = msg->len;
+       drv_data->rc = 0;
+       drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK |
+               MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN;
+
+       if (msg->flags & I2C_M_RD)
+               dir = 1;
+
+       if (msg->flags & I2C_M_REV_DIR_ADDR)
+               dir ^= 1;
+
+       if (msg->flags & I2C_M_TEN) {
+               drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir;
+               drv_data->addr2 = (u32)msg->addr & 0xff;
+       } else {
+               drv_data->addr1 = ((u32)msg->addr & 0x7f) << 1 | dir;
+               drv_data->addr2 = 0;
+       }
+}
+
+static void
+mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data)
+{
+       long            time_left;
+       unsigned long   flags;
+       char            abort = 0;
+
+       time_left = wait_event_interruptible_timeout(drv_data->waitq,
+               !drv_data->block, msecs_to_jiffies(drv_data->adapter.timeout));
+
+       spin_lock_irqsave(&drv_data->lock, flags);
+       if (!time_left) { /* Timed out */
+               drv_data->rc = -ETIMEDOUT;
+               abort = 1;
+       } else if (time_left < 0) { /* Interrupted/Error */
+               drv_data->rc = time_left; /* errno value */
+               abort = 1;
+       }
+
+       if (abort && drv_data->block) {
+               drv_data->state = MV64XXX_I2C_STATE_ABORTING;
+               spin_unlock_irqrestore(&drv_data->lock, flags);
+
+               time_left = wait_event_timeout(drv_data->waitq,
+                       !drv_data->block,
+                       msecs_to_jiffies(drv_data->adapter.timeout));
+
+               if (time_left <= 0) {
+                       drv_data->state = MV64XXX_I2C_STATE_IDLE;
+                       dev_err(&drv_data->adapter.dev,
+                               "mv64xxx: I2C bus locked\n");
+               }
+       } else
+               spin_unlock_irqrestore(&drv_data->lock, flags);
+}
+
+static int
+mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg)
+{
+       unsigned long   flags;
+
+       spin_lock_irqsave(&drv_data->lock, flags);
+       mv64xxx_i2c_prepare_for_io(drv_data, msg);
+
+       if (unlikely(msg->flags & I2C_M_NOSTART)) { /* Skip start/addr phases */
+               if (drv_data->msg->flags & I2C_M_RD) {
+                       /* No action to do, wait for slave to send a byte */
+                       drv_data->action = MV64XXX_I2C_ACTION_CONTINUE;
+                       drv_data->state =
+                               MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA;
+               } else {
+                       drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA;
+                       drv_data->state =
+                               MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK;
+                       drv_data->bytes_left--;
+               }
+       } else {
+               drv_data->action = MV64XXX_I2C_ACTION_SEND_START;
+               drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND;
+       }
+
+       drv_data->block = 1;
+       mv64xxx_i2c_do_action(drv_data);
+       spin_unlock_irqrestore(&drv_data->lock, flags);
+
+       mv64xxx_i2c_wait_for_completion(drv_data);
+       return drv_data->rc;
+}
+
+/*
+ *****************************************************************************
+ *
+ *     I2C Core Support Routines (Interface to higher level I2C code)
+ *
+ *****************************************************************************
+ */
+static u32
+mv64xxx_i2c_functionality(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
+}
+
+static int
+mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+       struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
+       int     i, rc = 0;
+
+       for (i=0; i<num; i++)
+               if ((rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i])) != 0)
+                       break;
+
+       return rc;
+}
+
+static struct i2c_algorithm mv64xxx_i2c_algo = {
+       .name = MV64XXX_I2C_CTLR_NAME " algorithm",
+       .id = I2C_ALGO_MV64XXX,
+       .master_xfer = mv64xxx_i2c_xfer,
+       .functionality = mv64xxx_i2c_functionality,
+};
+
+/*
+ *****************************************************************************
+ *
+ *     Driver Interface & Early Init Routines
+ *
+ *****************************************************************************
+ */
+static void __devinit
+mv64xxx_i2c_hw_init(struct mv64xxx_i2c_data *drv_data)
+{
+       writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SOFT_RESET);
+       writel((((drv_data->freq_m & 0xf) << 3) | (drv_data->freq_n & 0x7)),
+               drv_data->reg_base + MV64XXX_I2C_REG_BAUD);
+       writel(0, drv_data->reg_base + MV64XXX_I2C_REG_SLAVE_ADDR);
+       writel(0, drv_data->reg_base + MV64XXX_I2C_REG_EXT_SLAVE_ADDR);
+       writel(MV64XXX_I2C_REG_CONTROL_TWSIEN | MV64XXX_I2C_REG_CONTROL_STOP,
+               drv_data->reg_base + MV64XXX_I2C_REG_CONTROL);
+       drv_data->state = MV64XXX_I2C_STATE_IDLE;
+}
+
+static int __devinit
+mv64xxx_i2c_map_regs(struct platform_device *pd,
+       struct mv64xxx_i2c_data *drv_data)
+{
+       struct resource *r;
+
+       if ((r = platform_get_resource(pd, IORESOURCE_MEM, 0)) &&
+               request_mem_region(r->start, MV64XXX_I2C_REG_BLOCK_SIZE,
+                       drv_data->adapter.name)) {
+
+               drv_data->reg_base = ioremap(r->start,
+                       MV64XXX_I2C_REG_BLOCK_SIZE);
+               drv_data->reg_base_p = r->start;
+       } else
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void __devexit
+mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data)
+{
+       if (drv_data->reg_base) {
+               iounmap(drv_data->reg_base);
+               release_mem_region(drv_data->reg_base_p,
+                       MV64XXX_I2C_REG_BLOCK_SIZE);
+       }
+
+       drv_data->reg_base = NULL;
+       drv_data->reg_base_p = 0;
+}
+
+static int __devinit
+mv64xxx_i2c_probe(struct device *dev)
+{
+       struct platform_device          *pd = to_platform_device(dev);
+       struct mv64xxx_i2c_data         *drv_data;
+       struct mv64xxx_i2c_pdata        *pdata = dev->platform_data;
+       int     rc;
+
+       if ((pd->id != 0) || !pdata)
+               return -ENODEV;
+
+       drv_data = kmalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL);
+
+       if (!drv_data)
+               return -ENOMEM;
+
+       memset(drv_data, 0, sizeof(struct mv64xxx_i2c_data));
+
+       if (mv64xxx_i2c_map_regs(pd, drv_data)) {
+               rc = -ENODEV;
+               goto exit_kfree;
+       }
+
+       strncpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter",
+               I2C_NAME_SIZE);
+
+       init_waitqueue_head(&drv_data->waitq);
+       spin_lock_init(&drv_data->lock);
+
+       drv_data->freq_m = pdata->freq_m;
+       drv_data->freq_n = pdata->freq_n;
+       drv_data->irq = platform_get_irq(pd, 0);
+       drv_data->adapter.id = I2C_ALGO_MV64XXX | I2C_HW_MV64XXX;
+       drv_data->adapter.algo = &mv64xxx_i2c_algo;
+       drv_data->adapter.owner = THIS_MODULE;
+       drv_data->adapter.class = I2C_CLASS_HWMON;
+       drv_data->adapter.timeout = pdata->timeout;
+       drv_data->adapter.retries = pdata->retries;
+       dev_set_drvdata(dev, drv_data);
+       i2c_set_adapdata(&drv_data->adapter, drv_data);
+
+       if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0,
+               MV64XXX_I2C_CTLR_NAME, drv_data)) {
+
+               dev_err(dev, "mv64xxx: Can't register intr handler "
+                       "irq: %d\n", drv_data->irq);
+               rc = -EINVAL;
+               goto exit_unmap_regs;
+       } else if ((rc = i2c_add_adapter(&drv_data->adapter)) != 0) {
+               dev_err(dev, "mv64xxx: Can't add i2c adapter, rc: %d\n", -rc);
+               goto exit_free_irq;
+       }
+
+       mv64xxx_i2c_hw_init(drv_data);
+
+       return 0;
+
+       exit_free_irq:
+               free_irq(drv_data->irq, drv_data);
+       exit_unmap_regs:
+               mv64xxx_i2c_unmap_regs(drv_data);
+       exit_kfree:
+               kfree(drv_data);
+       return rc;
+}
+
+static int __devexit
+mv64xxx_i2c_remove(struct device *dev)
+{
+       struct mv64xxx_i2c_data         *drv_data = dev_get_drvdata(dev);
+       int     rc;
+
+       rc = i2c_del_adapter(&drv_data->adapter);
+       free_irq(drv_data->irq, drv_data);
+       mv64xxx_i2c_unmap_regs(drv_data);
+       kfree(drv_data);
+
+       return rc;
+}
+
+static struct device_driver mv64xxx_i2c_driver = {
+       .name   = MV64XXX_I2C_CTLR_NAME,
+       .bus    = &platform_bus_type,
+       .probe  = mv64xxx_i2c_probe,
+       .remove = mv64xxx_i2c_remove,
+};
+
+static int __init
+mv64xxx_i2c_init(void)
+{
+       return driver_register(&mv64xxx_i2c_driver);
+}
+
+static void __exit
+mv64xxx_i2c_exit(void)
+{
+       driver_unregister(&mv64xxx_i2c_driver);
+}
+
+module_init(mv64xxx_i2c_init);
+module_exit(mv64xxx_i2c_exit);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("Marvell mv64xxx host bridge i2c ctlr driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/chips/ds1337.c b/drivers/i2c/chips/ds1337.c
new file mode 100644 (file)
index 0000000..07f16c3
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ *  linux/drivers/i2c/chips/ds1337.c
+ *
+ *  Copyright (C) 2005 James Chapman <jchapman@katalix.com>
+ *
+ *     based on linux/drivers/acron/char/pcf8583.c
+ *  Copyright (C) 2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Driver for Dallas Semiconductor DS1337 real time clock chip
+ */
+
+#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/string.h>
+#include <linux/rtc.h>         /* get the user-level API */
+#include <linux/bcd.h>
+#include <linux/list.h>
+
+/* Device registers */
+#define DS1337_REG_HOUR                2
+#define DS1337_REG_DAY         3
+#define DS1337_REG_DATE                4
+#define DS1337_REG_MONTH       5
+#define DS1337_REG_CONTROL     14
+#define DS1337_REG_STATUS      15
+
+/* FIXME - how do we export these interface constants? */
+#define DS1337_GET_DATE                0
+#define DS1337_SET_DATE                1
+
+/*
+ * Functions declaration
+ */
+static unsigned short normal_i2c[] = { 0x68, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+
+SENSORS_INSMOD_1(ds1337);
+
+static int ds1337_attach_adapter(struct i2c_adapter *adapter);
+static int ds1337_detect(struct i2c_adapter *adapter, int address, int kind);
+static void ds1337_init_client(struct i2c_client *client);
+static int ds1337_detach_client(struct i2c_client *client);
+static int ds1337_command(struct i2c_client *client, unsigned int cmd,
+                         void *arg);
+
+/*
+ * Driver data (common to all clients)
+ */
+static struct i2c_driver ds1337_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "ds1337",
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = ds1337_attach_adapter,
+       .detach_client  = ds1337_detach_client,
+       .command        = ds1337_command,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+struct ds1337_data {
+       struct i2c_client client;
+       struct list_head list;
+       int id;
+};
+
+/*
+ * Internal variables
+ */
+static int ds1337_id;
+static LIST_HEAD(ds1337_clients);
+
+static inline int ds1337_read(struct i2c_client *client, u8 reg, u8 *value)
+{
+       s32 tmp = i2c_smbus_read_byte_data(client, reg);
+
+       if (tmp < 0)
+               return -EIO;
+
+       *value = tmp;
+
+       return 0;
+}
+
+/*
+ * Chip access functions
+ */
+static int ds1337_get_datetime(struct i2c_client *client, struct rtc_time *dt)
+{
+       struct ds1337_data *data = i2c_get_clientdata(client);
+       int result;
+       u8 buf[7];
+       u8 val;
+       struct i2c_msg msg[2];
+       u8 offs = 0;
+
+       if (!dt) {
+               dev_dbg(&client->adapter->dev, "%s: EINVAL: dt=NULL\n",
+                       __FUNCTION__);
+
+               return -EINVAL;
+       }
+
+       msg[0].addr = client->addr;
+       msg[0].flags = 0;
+       msg[0].len = 1;
+       msg[0].buf = &offs;
+
+       msg[1].addr = client->addr;
+       msg[1].flags = I2C_M_RD;
+       msg[1].len = sizeof(buf);
+       msg[1].buf = &buf[0];
+
+       result = client->adapter->algo->master_xfer(client->adapter,
+                                                   &msg[0], 2);
+
+       dev_dbg(&client->adapter->dev,
+               "%s: [%d] %02x %02x %02x %02x %02x %02x %02x\n",
+               __FUNCTION__, result, buf[0], buf[1], buf[2], buf[3],
+               buf[4], buf[5], buf[6]);
+
+       if (result >= 0) {
+               dt->tm_sec = BCD_TO_BIN(buf[0]);
+               dt->tm_min = BCD_TO_BIN(buf[1]);
+               val = buf[2] & 0x3f;
+               dt->tm_hour = BCD_TO_BIN(val);
+               dt->tm_wday = BCD_TO_BIN(buf[3]) - 1;
+               dt->tm_mday = BCD_TO_BIN(buf[4]);
+               val = buf[5] & 0x7f;
+               dt->tm_mon = BCD_TO_BIN(val);
+               dt->tm_year = 1900 + BCD_TO_BIN(buf[6]);
+               if (buf[5] & 0x80)
+                       dt->tm_year += 100;
+
+               dev_dbg(&client->adapter->dev, "%s: secs=%d, mins=%d, "
+                       "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+                       __FUNCTION__, dt->tm_sec, dt->tm_min,
+                       dt->tm_hour, dt->tm_mday,
+                       dt->tm_mon, dt->tm_year, dt->tm_wday);
+       } else {
+               dev_err(&client->adapter->dev, "ds1337[%d]: error reading "
+                       "data! %d\n", data->id, result);
+               result = -EIO;
+       }
+
+       return result;
+}
+
+static int ds1337_set_datetime(struct i2c_client *client, struct rtc_time *dt)
+{
+       struct ds1337_data *data = i2c_get_clientdata(client);
+       int result;
+       u8 buf[8];
+       u8 val;
+       struct i2c_msg msg[1];
+
+       if (!dt) {
+               dev_dbg(&client->adapter->dev, "%s: EINVAL: dt=NULL\n",
+                       __FUNCTION__);
+
+               return -EINVAL;
+       }
+
+       dev_dbg(&client->adapter->dev, "%s: secs=%d, mins=%d, hours=%d, "
+               "mday=%d, mon=%d, year=%d, wday=%d\n", __FUNCTION__,
+               dt->tm_sec, dt->tm_min, dt->tm_hour,
+               dt->tm_mday, dt->tm_mon, dt->tm_year, dt->tm_wday);
+
+       buf[0] = 0;             /* reg offset */
+       buf[1] = BIN_TO_BCD(dt->tm_sec);
+       buf[2] = BIN_TO_BCD(dt->tm_min);
+       buf[3] = BIN_TO_BCD(dt->tm_hour) | (1 << 6);
+       buf[4] = BIN_TO_BCD(dt->tm_wday) + 1;
+       buf[5] = BIN_TO_BCD(dt->tm_mday);
+       buf[6] = BIN_TO_BCD(dt->tm_mon);
+       if (dt->tm_year >= 2000) {
+               val = dt->tm_year - 2000;
+               buf[6] |= (1 << 7);
+       } else {
+               val = dt->tm_year - 1900;
+       }
+       buf[7] = BIN_TO_BCD(val);
+
+       msg[0].addr = client->addr;
+       msg[0].flags = 0;
+       msg[0].len = sizeof(buf);
+       msg[0].buf = &buf[0];
+
+       result = client->adapter->algo->master_xfer(client->adapter,
+                                                   &msg[0], 1);
+       if (result < 0) {
+               dev_err(&client->adapter->dev, "ds1337[%d]: error "
+                       "writing data! %d\n", data->id, result);
+               result = -EIO;
+       } else {
+               result = 0;
+       }
+
+       return result;
+}
+
+static int ds1337_command(struct i2c_client *client, unsigned int cmd,
+                         void *arg)
+{
+       dev_dbg(&client->adapter->dev, "%s: cmd=%d\n", __FUNCTION__, cmd);
+
+       switch (cmd) {
+       case DS1337_GET_DATE:
+               return ds1337_get_datetime(client, arg);
+
+       case DS1337_SET_DATE:
+               return ds1337_set_datetime(client, arg);
+
+       default:
+               return -EINVAL;
+       }
+}
+
+/*
+ * Public API for access to specific device. Useful for low-level
+ * RTC access from kernel code.
+ */
+int ds1337_do_command(int id, int cmd, void *arg)
+{
+       struct list_head *walk;
+       struct list_head *tmp;
+       struct ds1337_data *data;
+
+       list_for_each_safe(walk, tmp, &ds1337_clients) {
+               data = list_entry(walk, struct ds1337_data, list);
+               if (data->id == id)
+                       return ds1337_command(&data->client, cmd, arg);
+       }
+
+       return -ENODEV;
+}
+
+static int ds1337_attach_adapter(struct i2c_adapter *adapter)
+{
+       return i2c_detect(adapter, &addr_data, ds1337_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+static int ds1337_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *new_client;
+       struct ds1337_data *data;
+       int err = 0;
+       const char *name = "";
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+                                    I2C_FUNC_I2C))
+               goto exit;
+
+       if (!(data = kmalloc(sizeof(struct ds1337_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto exit;
+       }
+       memset(data, 0, sizeof(struct ds1337_data));
+       INIT_LIST_HEAD(&data->list);
+
+       /* The common I2C client data is placed right before the
+        * DS1337-specific data. 
+        */
+       new_client = &data->client;
+       i2c_set_clientdata(new_client, data);
+       new_client->addr = address;
+       new_client->adapter = adapter;
+       new_client->driver = &ds1337_driver;
+       new_client->flags = 0;
+
+       /*
+        * Now we do the remaining detection. A negative kind means that
+        * the driver was loaded with no force parameter (default), so we
+        * must both detect and identify the chip. A zero kind means that
+        * the driver was loaded with the force parameter, the detection
+        * step shall be skipped. A positive kind means that the driver
+        * was loaded with the force parameter and a given kind of chip is
+        * requested, so both the detection and the identification steps
+        * are skipped.
+        *
+        * For detection, we read registers that are most likely to cause
+        * detection failure, i.e. those that have more bits with fixed
+        * or reserved values.
+        */
+
+       /* Default to an DS1337 if forced */
+       if (kind == 0)
+               kind = ds1337;
+
+       if (kind < 0) {         /* detection and identification */
+               u8 data;
+
+               /* Check that status register bits 6-2 are zero */
+               if ((ds1337_read(new_client, DS1337_REG_STATUS, &data) < 0) ||
+                   (data & 0x7c))
+                       goto exit_free;
+
+               /* Check for a valid day register value */
+               if ((ds1337_read(new_client, DS1337_REG_DAY, &data) < 0) ||
+                   (data == 0) || (data & 0xf8))
+                       goto exit_free;
+
+               /* Check for a valid date register value */
+               if ((ds1337_read(new_client, DS1337_REG_DATE, &data) < 0) ||
+                   (data == 0) || (data & 0xc0) || ((data & 0x0f) > 9) ||
+                   (data >= 0x32))
+                       goto exit_free;
+
+               /* Check for a valid month register value */
+               if ((ds1337_read(new_client, DS1337_REG_MONTH, &data) < 0) ||
+                   (data == 0) || (data & 0x60) || ((data & 0x0f) > 9) ||
+                   ((data >= 0x13) && (data <= 0x19)))
+                       goto exit_free;
+
+               /* Check that control register bits 6-5 are zero */
+               if ((ds1337_read(new_client, DS1337_REG_CONTROL, &data) < 0) ||
+                   (data & 0x60))
+                       goto exit_free;
+
+               kind = ds1337;
+       }
+
+       if (kind == ds1337)
+               name = "ds1337";
+
+       /* We can fill in the remaining client fields */
+       strlcpy(new_client->name, name, I2C_NAME_SIZE);
+
+       /* Tell the I2C layer a new client has arrived */
+       if ((err = i2c_attach_client(new_client)))
+               goto exit_free;
+
+       /* Initialize the DS1337 chip */
+       ds1337_init_client(new_client);
+
+       /* Add client to local list */
+       data->id = ds1337_id++;
+       list_add(&data->list, &ds1337_clients);
+
+       return 0;
+
+exit_free:
+       kfree(data);
+exit:
+       return err;
+}
+
+static void ds1337_init_client(struct i2c_client *client)
+{
+       s32 val;
+
+       /* Ensure that device is set in 24-hour mode */
+       val = i2c_smbus_read_byte_data(client, DS1337_REG_HOUR);
+       if ((val >= 0) && (val & (1 << 6)) == 0)
+               i2c_smbus_write_byte_data(client, DS1337_REG_HOUR,
+                                         val | (1 << 6));
+}
+
+static int ds1337_detach_client(struct i2c_client *client)
+{
+       int err;
+       struct ds1337_data *data = i2c_get_clientdata(client);
+
+       if ((err = i2c_detach_client(client))) {
+               dev_err(&client->dev, "Client deregistration failed, "
+                       "client not detached.\n");
+               return err;
+       }
+
+       list_del(&data->list);
+       kfree(data);
+       return 0;
+}
+
+static int __init ds1337_init(void)
+{
+       return i2c_add_driver(&ds1337_driver);
+}
+
+static void __exit ds1337_exit(void)
+{
+       i2c_del_driver(&ds1337_driver);
+}
+
+MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
+MODULE_DESCRIPTION("DS1337 RTC driver");
+MODULE_LICENSE("GPL");
+
+module_init(ds1337_init);
+module_exit(ds1337_exit);
diff --git a/drivers/i2c/chips/fscpos.c b/drivers/i2c/chips/fscpos.c
new file mode 100644 (file)
index 0000000..2cac791
--- /dev/null
@@ -0,0 +1,641 @@
+/*
+       fscpos.c - Kernel module for hardware monitoring with FSC Poseidon chips
+       Copyright (C) 2004, 2005 Stefan Ott <stefan@desire.ch>
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the 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.
+*/
+
+/*
+       fujitsu siemens poseidon chip,
+       module based on the old fscpos module by Hermann Jung <hej@odn.de> and
+       the fscher module by Reinhard Nissl <rnissl@gmx.de>
+
+       original module based on lm80.c
+       Copyright (C) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
+       and Philip Edelbrock <phil@netroedge.com>
+
+       Thanks to Jean Delvare for reviewing my code and suggesting a lot of
+       improvements.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/init.h>
+
+/*
+ * Addresses to scan
+ */
+static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+
+/*
+ * Insmod parameters
+ */
+SENSORS_INSMOD_1(fscpos);
+
+/*
+ * The FSCPOS registers
+ */
+
+/* chip identification */
+#define FSCPOS_REG_IDENT_0             0x00
+#define FSCPOS_REG_IDENT_1             0x01
+#define FSCPOS_REG_IDENT_2             0x02
+#define FSCPOS_REG_REVISION            0x03
+
+/* global control and status */
+#define FSCPOS_REG_EVENT_STATE         0x04
+#define FSCPOS_REG_CONTROL             0x05
+
+/* watchdog */
+#define FSCPOS_REG_WDOG_PRESET         0x28
+#define FSCPOS_REG_WDOG_STATE          0x23
+#define FSCPOS_REG_WDOG_CONTROL                0x21
+
+/* voltages */
+#define FSCPOS_REG_VOLT_12             0x45
+#define FSCPOS_REG_VOLT_5              0x42
+#define FSCPOS_REG_VOLT_BATT           0x48
+
+/* fans - the chip does not support minimum speed for fan2 */
+static u8 FSCPOS_REG_PWM[] = { 0x55, 0x65 };
+static u8 FSCPOS_REG_FAN_ACT[] = { 0x0e, 0x6b, 0xab };
+static u8 FSCPOS_REG_FAN_STATE[] = { 0x0d, 0x62, 0xa2 };
+static u8 FSCPOS_REG_FAN_RIPPLE[] = { 0x0f, 0x6f, 0xaf };
+
+/* temperatures */
+static u8 FSCPOS_REG_TEMP_ACT[] = { 0x64, 0x32, 0x35 };
+static u8 FSCPOS_REG_TEMP_STATE[] = { 0x71, 0x81, 0x91 };
+
+/*
+ * Functions declaration
+ */
+static int fscpos_attach_adapter(struct i2c_adapter *adapter);
+static int fscpos_detect(struct i2c_adapter *adapter, int address, int kind);
+static int fscpos_detach_client(struct i2c_client *client);
+
+static int fscpos_read_value(struct i2c_client *client, u8 register);
+static int fscpos_write_value(struct i2c_client *client, u8 register, u8 value);
+static struct fscpos_data *fscpos_update_device(struct device *dev);
+static void fscpos_init_client(struct i2c_client *client);
+
+static void reset_fan_alarm(struct i2c_client *client, int nr);
+
+/*
+ * Driver data (common to all clients)
+ */
+static struct i2c_driver fscpos_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "fscpos",
+       .id             = I2C_DRIVERID_FSCPOS,
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = fscpos_attach_adapter,
+       .detach_client  = fscpos_detach_client,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+struct fscpos_data {
+       struct i2c_client client;
+       struct semaphore update_lock;
+       char valid;             /* 0 until following fields are valid */
+       unsigned long last_updated;     /* In jiffies */
+
+       /* register values */
+       u8 revision;            /* revision of chip */
+       u8 global_event;        /* global event status */
+       u8 global_control;      /* global control register */
+       u8 wdog_control;        /* watchdog control */
+       u8 wdog_state;          /* watchdog status */
+       u8 wdog_preset;         /* watchdog preset */
+       u8 volt[3];             /* 12, 5, battery current */
+       u8 temp_act[3];         /* temperature */
+       u8 temp_status[3];      /* status of sensor */
+       u8 fan_act[3];          /* fans revolutions per second */
+       u8 fan_status[3];       /* fan status */
+       u8 pwm[2];              /* fan min value for rps */
+       u8 fan_ripple[3];       /* divider for rps */
+};
+
+/* Temperature */
+#define TEMP_FROM_REG(val)     (((val) - 128) * 1000)
+
+static ssize_t show_temp_input(struct fscpos_data *data, char *buf, int nr)
+{
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[nr - 1]));
+}
+
+static ssize_t show_temp_status(struct fscpos_data *data, char *buf, int nr)
+{
+       /* bits 2..7 reserved => mask with 0x03 */
+       return sprintf(buf, "%u\n", data->temp_status[nr - 1] & 0x03);
+}
+
+static ssize_t show_temp_reset(struct fscpos_data *data, char *buf, int nr)
+{
+       return sprintf(buf, "1\n");
+}
+
+static ssize_t set_temp_reset(struct i2c_client *client, struct fscpos_data
+                       *data, const char *buf, size_t count, int nr, int reg)
+{
+       unsigned long v = simple_strtoul(buf, NULL, 10);
+       if (v != 1) {
+               dev_err(&client->dev, "temp_reset value %ld not supported. "
+                                       "Use 1 to reset the alarm!\n", v);
+               return -EINVAL;
+       }
+
+       dev_info(&client->dev, "You used the temp_reset feature which has not "
+                               "been proplerly tested. Please report your "
+                               "experience to the module author.\n");
+
+       /* Supported value: 2 (clears the status) */
+       fscpos_write_value(client, FSCPOS_REG_TEMP_STATE[nr], 2);
+       return count;
+}
+
+/* Fans */
+#define RPM_FROM_REG(val)      ((val) * 60)
+
+static ssize_t show_fan_status(struct fscpos_data *data, char *buf, int nr)
+{
+       /* bits 0..1, 3..7 reserved => mask with 0x04 */
+       return sprintf(buf, "%u\n", data->fan_status[nr - 1] & 0x04);
+}
+
+static ssize_t show_fan_input(struct fscpos_data *data, char *buf, int nr)
+{
+       return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[nr - 1]));
+}
+
+static ssize_t show_fan_ripple(struct fscpos_data *data, char *buf, int nr)
+{
+       /* bits 2..7 reserved => mask with 0x03 */
+       return sprintf(buf, "%u\n", data->fan_ripple[nr - 1] & 0x03);
+}
+
+static ssize_t set_fan_ripple(struct i2c_client *client, struct fscpos_data
+                       *data, const char *buf, size_t count, int nr, int reg)
+{
+       /* supported values: 2, 4, 8 */
+       unsigned long v = simple_strtoul(buf, NULL, 10);
+
+       switch (v) {
+               case 2: v = 1; break;
+               case 4: v = 2; break;
+               case 8: v = 3; break;
+       default:
+               dev_err(&client->dev, "fan_ripple value %ld not supported. "
+                                       "Must be one of 2, 4 or 8!\n", v);
+               return -EINVAL;
+       }
+       
+       down(&data->update_lock);
+       /* bits 2..7 reserved => mask with 0x03 */
+       data->fan_ripple[nr - 1] &= ~0x03;
+       data->fan_ripple[nr - 1] |= v;
+       
+       fscpos_write_value(client, reg, data->fan_ripple[nr - 1]);
+       up(&data->update_lock);
+       return count;
+}
+
+static ssize_t show_pwm(struct fscpos_data *data, char *buf, int nr)
+{
+       return sprintf(buf, "%u\n", data->pwm[nr - 1]);
+}
+
+static ssize_t set_pwm(struct i2c_client *client, struct fscpos_data *data,
+                               const char *buf, size_t count, int nr, int reg)
+{
+       unsigned long v = simple_strtoul(buf, NULL, 10);
+
+       /* Range: 0..255 */
+       if (v < 0) v = 0;
+       if (v > 255) v = 255;
+
+       down(&data->update_lock);
+       data->pwm[nr - 1] = v;
+       fscpos_write_value(client, reg, data->pwm[nr - 1]);
+       up(&data->update_lock);
+       return count;
+}
+
+static void reset_fan_alarm(struct i2c_client *client, int nr)
+{
+       fscpos_write_value(client, FSCPOS_REG_FAN_STATE[nr], 4);
+}
+
+/* Volts */
+#define VOLT_FROM_REG(val, mult)       ((val) * (mult) / 255)
+
+static ssize_t show_volt_12(struct device *dev, char *buf)
+{
+       struct fscpos_data *data = fscpos_update_device(dev);
+       return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[0], 14200));
+}
+
+static ssize_t show_volt_5(struct device *dev, char *buf)
+{
+       struct fscpos_data *data = fscpos_update_device(dev);
+       return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[1], 6600));
+}
+
+static ssize_t show_volt_batt(struct device *dev, char *buf)
+{
+       struct fscpos_data *data = fscpos_update_device(dev);
+       return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[2], 3300));
+}
+
+/* Watchdog */
+static ssize_t show_wdog_control(struct fscpos_data *data, char *buf)
+{
+       /* bits 0..3 reserved, bit 6 write only => mask with 0xb0 */
+       return sprintf(buf, "%u\n", data->wdog_control & 0xb0);
+}
+
+static ssize_t set_wdog_control(struct i2c_client *client, struct fscpos_data
+                               *data, const char *buf, size_t count, int reg)
+{
+       /* bits 0..3 reserved => mask with 0xf0 */
+       unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0;
+
+       down(&data->update_lock);
+       data->wdog_control &= ~0xf0;
+       data->wdog_control |= v;
+       fscpos_write_value(client, reg, data->wdog_control);
+       up(&data->update_lock);
+       return count;
+}
+
+static ssize_t show_wdog_state(struct fscpos_data *data, char *buf)
+{
+       /* bits 0, 2..7 reserved => mask with 0x02 */
+       return sprintf(buf, "%u\n", data->wdog_state & 0x02);
+}
+
+static ssize_t set_wdog_state(struct i2c_client *client, struct fscpos_data
+                               *data, const char *buf, size_t count, int reg)
+{
+       unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02;
+
+       /* Valid values: 2 (clear) */
+       if (v != 2) {
+               dev_err(&client->dev, "wdog_state value %ld not supported. "
+                                       "Must be 2 to clear the state!\n", v);
+               return -EINVAL;
+       }
+
+       down(&data->update_lock);
+       data->wdog_state &= ~v;
+       fscpos_write_value(client, reg, v);
+       up(&data->update_lock);
+       return count;
+}
+
+static ssize_t show_wdog_preset(struct fscpos_data *data, char *buf)
+{
+       return sprintf(buf, "%u\n", data->wdog_preset);
+}
+
+static ssize_t set_wdog_preset(struct i2c_client *client, struct fscpos_data
+                               *data, const char *buf, size_t count, int reg)
+{
+       unsigned long v = simple_strtoul(buf, NULL, 10) & 0xff;
+
+       down(&data->update_lock);
+       data->wdog_preset = v;
+       fscpos_write_value(client, reg, data->wdog_preset);
+       up(&data->update_lock);
+       return count;
+}
+
+/* Event */
+static ssize_t show_event(struct device *dev, char *buf)
+{
+       /* bits 5..7 reserved => mask with 0x1f */
+       struct fscpos_data *data = fscpos_update_device(dev);
+       return sprintf(buf, "%u\n", data->global_event & 0x9b);
+}
+
+/*
+ * Sysfs stuff
+ */
+#define create_getter(kind, sub) \
+       static ssize_t sysfs_show_##kind##sub(struct device *dev, char *buf) \
+       { \
+               struct fscpos_data *data = fscpos_update_device(dev); \
+               return show_##kind##sub(data, buf); \
+       }
+
+#define create_getter_n(kind, offset, sub) \
+       static ssize_t sysfs_show_##kind##offset##sub(struct device *dev, char\
+                                                                       *buf) \
+       { \
+               struct fscpos_data *data = fscpos_update_device(dev); \
+               return show_##kind##sub(data, buf, offset); \
+       }
+
+#define create_setter(kind, sub, reg) \
+       static ssize_t sysfs_set_##kind##sub (struct device *dev, const char \
+                                                       *buf, size_t count) \
+       { \
+               struct i2c_client *client = to_i2c_client(dev); \
+               struct fscpos_data *data = i2c_get_clientdata(client); \
+               return set_##kind##sub(client, data, buf, count, reg); \
+       }
+
+#define create_setter_n(kind, offset, sub, reg) \
+       static ssize_t sysfs_set_##kind##offset##sub (struct device *dev, \
+                                       const char *buf, size_t count) \
+       { \
+               struct i2c_client *client = to_i2c_client(dev); \
+               struct fscpos_data *data = i2c_get_clientdata(client); \
+               return set_##kind##sub(client, data, buf, count, offset, reg);\
+       }
+
+#define create_sysfs_device_ro(kind, sub, offset) \
+       static DEVICE_ATTR(kind##offset##sub, S_IRUGO, \
+                                       sysfs_show_##kind##offset##sub, NULL);
+
+#define create_sysfs_device_rw(kind, sub, offset) \
+       static DEVICE_ATTR(kind##offset##sub, S_IRUGO | S_IWUSR, \
+               sysfs_show_##kind##offset##sub, sysfs_set_##kind##offset##sub);
+
+#define sysfs_ro_n(kind, sub, offset) \
+       create_getter_n(kind, offset, sub); \
+       create_sysfs_device_ro(kind, sub, offset);
+
+#define sysfs_rw_n(kind, sub, offset, reg) \
+       create_getter_n(kind, offset, sub); \
+       create_setter_n(kind, offset, sub, reg); \
+       create_sysfs_device_rw(kind, sub, offset);
+
+#define sysfs_rw(kind, sub, reg) \
+       create_getter(kind, sub); \
+       create_setter(kind, sub, reg); \
+       create_sysfs_device_rw(kind, sub,);
+
+#define sysfs_fan_with_min(offset, reg_status, reg_ripple, reg_min) \
+       sysfs_fan(offset, reg_status, reg_ripple); \
+       sysfs_rw_n(pwm,, offset, reg_min);
+
+#define sysfs_fan(offset, reg_status, reg_ripple) \
+       sysfs_ro_n(fan, _input, offset); \
+       sysfs_ro_n(fan, _status, offset); \
+       sysfs_rw_n(fan, _ripple, offset, reg_ripple);
+
+#define sysfs_temp(offset, reg_status) \
+       sysfs_ro_n(temp, _input, offset); \
+       sysfs_ro_n(temp, _status, offset); \
+       sysfs_rw_n(temp, _reset, offset, reg_status);
+
+#define sysfs_watchdog(reg_wdog_preset, reg_wdog_state, reg_wdog_control) \
+       sysfs_rw(wdog, _control, reg_wdog_control); \
+       sysfs_rw(wdog, _preset, reg_wdog_preset); \
+       sysfs_rw(wdog, _state, reg_wdog_state);
+
+sysfs_fan_with_min(1, FSCPOS_REG_FAN_STATE[0], FSCPOS_REG_FAN_RIPPLE[0],
+                                                       FSCPOS_REG_PWM[0]);
+sysfs_fan_with_min(2, FSCPOS_REG_FAN_STATE[1], FSCPOS_REG_FAN_RIPPLE[1],
+                                                       FSCPOS_REG_PWM[1]);
+sysfs_fan(3, FSCPOS_REG_FAN_STATE[2], FSCPOS_REG_FAN_RIPPLE[2]);
+
+sysfs_temp(1, FSCPOS_REG_TEMP_STATE[0]);
+sysfs_temp(2, FSCPOS_REG_TEMP_STATE[1]);
+sysfs_temp(3, FSCPOS_REG_TEMP_STATE[2]);
+
+sysfs_watchdog(FSCPOS_REG_WDOG_PRESET, FSCPOS_REG_WDOG_STATE,
+                                               FSCPOS_REG_WDOG_CONTROL);
+
+static DEVICE_ATTR(event, S_IRUGO, show_event, NULL);
+static DEVICE_ATTR(in0_input, S_IRUGO, show_volt_12, NULL);
+static DEVICE_ATTR(in1_input, S_IRUGO, show_volt_5, NULL);
+static DEVICE_ATTR(in2_input, S_IRUGO, show_volt_batt, NULL);
+
+static int fscpos_attach_adapter(struct i2c_adapter *adapter)
+{
+       if (!(adapter->class & I2C_CLASS_HWMON))
+               return 0;
+       return i2c_detect(adapter, &addr_data, fscpos_detect);
+}
+
+int fscpos_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *new_client;
+       struct fscpos_data *data;
+       int err = 0;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               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 fscpos_{read,write}_value.
+        */
+
+       if (!(data = kmalloc(sizeof(struct fscpos_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto exit;
+       }
+       memset(data, 0, sizeof(struct fscpos_data));
+
+       new_client = &data->client;
+       i2c_set_clientdata(new_client, data);
+       new_client->addr = address;
+       new_client->adapter = adapter;
+       new_client->driver = &fscpos_driver;
+       new_client->flags = 0;
+
+       /* Do the remaining detection unless force or force_fscpos parameter */
+       if (kind < 0) {
+               if ((fscpos_read_value(new_client, FSCPOS_REG_IDENT_0)
+                       != 0x50) /* 'P' */
+               || (fscpos_read_value(new_client, FSCPOS_REG_IDENT_1)
+                       != 0x45) /* 'E' */
+               || (fscpos_read_value(new_client, FSCPOS_REG_IDENT_2)
+                       != 0x47))/* 'G' */
+               {
+                       dev_dbg(&new_client->dev, "fscpos detection failed\n");
+                       goto exit_free;
+               }
+       }
+
+       /* Fill in the remaining client fields and put it in the global list */
+       strlcpy(new_client->name, "fscpos", 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;
+
+       /* Inizialize the fscpos chip */
+       fscpos_init_client(new_client);
+
+       /* Announce that the chip was found */
+       dev_info(&new_client->dev, "Found fscpos chip, rev %u\n", data->revision);
+
+       /* Register sysfs hooks */
+       device_create_file(&new_client->dev, &dev_attr_event);
+       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_wdog_control);
+       device_create_file(&new_client->dev, &dev_attr_wdog_preset);
+       device_create_file(&new_client->dev, &dev_attr_wdog_state);
+       device_create_file(&new_client->dev, &dev_attr_temp1_input);
+       device_create_file(&new_client->dev, &dev_attr_temp1_status);
+       device_create_file(&new_client->dev, &dev_attr_temp1_reset);
+       device_create_file(&new_client->dev, &dev_attr_temp2_input);
+       device_create_file(&new_client->dev, &dev_attr_temp2_status);
+       device_create_file(&new_client->dev, &dev_attr_temp2_reset);
+       device_create_file(&new_client->dev, &dev_attr_temp3_input);
+       device_create_file(&new_client->dev, &dev_attr_temp3_status);
+       device_create_file(&new_client->dev, &dev_attr_temp3_reset);
+       device_create_file(&new_client->dev, &dev_attr_fan1_input);
+       device_create_file(&new_client->dev, &dev_attr_fan1_status);
+       device_create_file(&new_client->dev, &dev_attr_fan1_ripple);
+       device_create_file(&new_client->dev, &dev_attr_pwm1);
+       device_create_file(&new_client->dev, &dev_attr_fan2_input);
+       device_create_file(&new_client->dev, &dev_attr_fan2_status);
+       device_create_file(&new_client->dev, &dev_attr_fan2_ripple);
+       device_create_file(&new_client->dev, &dev_attr_pwm2);
+       device_create_file(&new_client->dev, &dev_attr_fan3_input);
+       device_create_file(&new_client->dev, &dev_attr_fan3_status);
+       device_create_file(&new_client->dev, &dev_attr_fan3_ripple);
+
+       return 0;
+
+exit_free:
+       kfree(data);
+exit:
+       return err;
+}
+
+static int fscpos_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 int fscpos_read_value(struct i2c_client *client, u8 reg)
+{
+       dev_dbg(&client->dev, "Read reg 0x%02x\n", reg);
+       return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+       dev_dbg(&client->dev, "Write reg 0x%02x, val 0x%02x\n", reg, value);
+       return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Called when we have found a new FSCPOS chip */
+static void fscpos_init_client(struct i2c_client *client)
+{
+       struct fscpos_data *data = i2c_get_clientdata(client);
+
+       /* read revision from chip */
+       data->revision = fscpos_read_value(client, FSCPOS_REG_REVISION);
+}
+
+static struct fscpos_data *fscpos_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct fscpos_data *data = i2c_get_clientdata(client);
+
+       down(&data->update_lock);
+
+       if ((jiffies - data->last_updated > 2 * HZ) ||
+                       (jiffies < data->last_updated) || !data->valid) {
+               int i;
+
+               dev_dbg(&client->dev, "Starting fscpos update\n");
+
+               for (i = 0; i < 3; i++) {
+                       data->temp_act[i] = fscpos_read_value(client,
+                                               FSCPOS_REG_TEMP_ACT[i]);
+                       data->temp_status[i] = fscpos_read_value(client,
+                                               FSCPOS_REG_TEMP_STATE[i]);
+                       data->fan_act[i] = fscpos_read_value(client,
+                                               FSCPOS_REG_FAN_ACT[i]);
+                       data->fan_status[i] = fscpos_read_value(client,
+                                               FSCPOS_REG_FAN_STATE[i]);
+                       data->fan_ripple[i] = fscpos_read_value(client,
+                                               FSCPOS_REG_FAN_RIPPLE[i]);
+                       if (i < 2) {
+                               /* fan2_min is not supported by the chip */
+                               data->pwm[i] = fscpos_read_value(client,
+                                                       FSCPOS_REG_PWM[i]);
+                       }
+                       /* reset fan status if speed is back to > 0 */
+                       if (data->fan_status[i] != 0 && data->fan_act[i] > 0) {
+                               reset_fan_alarm(client, i);
+                       }
+               }
+
+               data->volt[0] = fscpos_read_value(client, FSCPOS_REG_VOLT_12);
+               data->volt[1] = fscpos_read_value(client, FSCPOS_REG_VOLT_5);
+               data->volt[2] = fscpos_read_value(client, FSCPOS_REG_VOLT_BATT);
+
+               data->wdog_preset = fscpos_read_value(client,
+                                                       FSCPOS_REG_WDOG_PRESET);
+               data->wdog_state = fscpos_read_value(client,
+                                                       FSCPOS_REG_WDOG_STATE);
+               data->wdog_control = fscpos_read_value(client,
+                                               FSCPOS_REG_WDOG_CONTROL);
+
+               data->global_event = fscpos_read_value(client,
+                                               FSCPOS_REG_EVENT_STATE);
+
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+       up(&data->update_lock);
+       return data;
+}
+
+static int __init sm_fscpos_init(void)
+{
+       return i2c_add_driver(&fscpos_driver);
+}
+
+static void __exit sm_fscpos_exit(void)
+{
+       i2c_del_driver(&fscpos_driver);
+}
+
+MODULE_AUTHOR("Stefan Ott <stefan@desire.ch> based on work from Hermann Jung "
+                               "<hej@odn.de>, Frodo Looijaard <frodol@dds.nl>"
+                               " and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("fujitsu siemens poseidon chip driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_fscpos_init);
+module_exit(sm_fscpos_exit);
diff --git a/drivers/i2c/chips/gl520sm.c b/drivers/i2c/chips/gl520sm.c
new file mode 100644 (file)
index 0000000..3fd17e4
--- /dev/null
@@ -0,0 +1,769 @@
+/*
+    gl520sm.c - Part of lm_sensors, Linux kernel modules for hardware
+                monitoring
+    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>,
+                              Kyösti Mälkki <kmalkki@cc.hut.fi>
+    Copyright (c) 2005        Maarten Deprez <maartendeprez@users.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/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/i2c-vid.h>
+
+/* Type of the extra sensor */
+static unsigned short extra_sensor_type;
+module_param(extra_sensor_type, ushort, 0);
+MODULE_PARM_DESC(extra_sensor_type, "Type of extra sensor (0=autodetect, 1=temperature, 2=voltage)");
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(gl520sm);
+
+/* Many GL520 constants specified below 
+One of the inputs can be configured as either temp or voltage.
+That's why _TEMP2 and _IN4 access the same register 
+*/
+
+/* The GL520 registers */
+#define GL520_REG_CHIP_ID              0x00
+#define GL520_REG_REVISION             0x01
+#define GL520_REG_CONF                 0x03
+#define GL520_REG_MASK                 0x11
+
+#define GL520_REG_VID_INPUT            0x02
+
+#define GL520_REG_IN0_INPUT            0x15
+#define GL520_REG_IN0_LIMIT            0x0c
+#define GL520_REG_IN0_MIN              GL520_REG_IN0_LIMIT
+#define GL520_REG_IN0_MAX              GL520_REG_IN0_LIMIT
+
+#define GL520_REG_IN1_INPUT            0x14
+#define GL520_REG_IN1_LIMIT            0x09
+#define GL520_REG_IN1_MIN              GL520_REG_IN1_LIMIT
+#define GL520_REG_IN1_MAX              GL520_REG_IN1_LIMIT
+
+#define GL520_REG_IN2_INPUT            0x13
+#define GL520_REG_IN2_LIMIT            0x0a
+#define GL520_REG_IN2_MIN              GL520_REG_IN2_LIMIT
+#define GL520_REG_IN2_MAX              GL520_REG_IN2_LIMIT
+
+#define GL520_REG_IN3_INPUT            0x0d
+#define GL520_REG_IN3_LIMIT            0x0b
+#define GL520_REG_IN3_MIN              GL520_REG_IN3_LIMIT
+#define GL520_REG_IN3_MAX              GL520_REG_IN3_LIMIT
+
+#define GL520_REG_IN4_INPUT            0x0e
+#define GL520_REG_IN4_MAX              0x17
+#define GL520_REG_IN4_MIN              0x18
+
+#define GL520_REG_TEMP1_INPUT          0x04
+#define GL520_REG_TEMP1_MAX            0x05
+#define GL520_REG_TEMP1_MAX_HYST       0x06
+
+#define GL520_REG_TEMP2_INPUT          0x0e
+#define GL520_REG_TEMP2_MAX            0x17
+#define GL520_REG_TEMP2_MAX_HYST       0x18
+
+#define GL520_REG_FAN_INPUT            0x07
+#define GL520_REG_FAN_MIN              0x08
+#define GL520_REG_FAN_DIV              0x0f
+#define GL520_REG_FAN_OFF              GL520_REG_FAN_DIV
+
+#define GL520_REG_ALARMS               0x12
+#define GL520_REG_BEEP_MASK            0x10
+#define GL520_REG_BEEP_ENABLE          GL520_REG_CONF
+
+/*
+ * Function declarations
+ */
+
+static int gl520_attach_adapter(struct i2c_adapter *adapter);
+static int gl520_detect(struct i2c_adapter *adapter, int address, int kind);
+static void gl520_init_client(struct i2c_client *client);
+static int gl520_detach_client(struct i2c_client *client);
+static int gl520_read_value(struct i2c_client *client, u8 reg);
+static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value);
+static struct gl520_data *gl520_update_device(struct device *dev);
+
+/* Driver data */
+static struct i2c_driver gl520_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "gl520sm",
+       .id             = I2C_DRIVERID_GL520,
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = gl520_attach_adapter,
+       .detach_client  = gl520_detach_client,
+};
+
+/* Client data */
+struct gl520_data {
+       struct i2c_client client;
+       struct semaphore update_lock;
+       char valid;             /* zero until the following fields are valid */
+       unsigned long last_updated;     /* in jiffies */
+
+       u8 vid;
+       u8 vrm;
+       u8 in_input[5];         /* [0] = VVD */
+       u8 in_min[5];           /* [0] = VDD */
+       u8 in_max[5];           /* [0] = VDD */
+       u8 fan_input[2];
+       u8 fan_min[2];
+       u8 fan_div[2];
+       u8 fan_off;
+       u8 temp_input[2];
+       u8 temp_max[2];
+       u8 temp_max_hyst[2];
+       u8 alarms;
+       u8 beep_enable;
+       u8 beep_mask;
+       u8 alarm_mask;
+       u8 two_temps;
+};
+
+/*
+ * Sysfs stuff
+ */
+
+#define sysfs_r(type, n, item, reg) \
+static ssize_t get_##type##item (struct gl520_data *, char *, int); \
+static ssize_t get_##type##n##item (struct device *, char *); \
+static ssize_t get_##type##n##item (struct device *dev, char *buf) \
+{ \
+       struct gl520_data *data = gl520_update_device(dev); \
+       return get_##type##item(data, buf, (n)); \
+}
+
+#define sysfs_w(type, n, item, reg) \
+static ssize_t set_##type##item (struct i2c_client *, struct gl520_data *, const char *, size_t, int, int); \
+static ssize_t set_##type##n##item (struct device *, const char *, size_t); \
+static ssize_t set_##type##n##item (struct device *dev, const char *buf, size_t count) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct gl520_data *data = i2c_get_clientdata(client); \
+       return set_##type##item(client, data, buf, count, (n), reg); \
+}
+
+#define sysfs_rw_n(type, n, item, reg) \
+sysfs_r(type, n, item, reg) \
+sysfs_w(type, n, item, reg) \
+static DEVICE_ATTR(type##n##item, S_IRUGO | S_IWUSR, get_##type##n##item, set_##type##n##item);
+
+#define sysfs_ro_n(type, n, item, reg) \
+sysfs_r(type, n, item, reg) \
+static DEVICE_ATTR(type##n##item, S_IRUGO, get_##type##n##item, NULL);
+
+#define sysfs_rw(type, item, reg) \
+sysfs_r(type, 0, item, reg) \
+sysfs_w(type, 0, item, reg) \
+static DEVICE_ATTR(type##item, S_IRUGO | S_IWUSR, get_##type##0##item, set_##type##0##item);
+
+#define sysfs_ro(type, item, reg) \
+sysfs_r(type, 0, item, reg) \
+static DEVICE_ATTR(type##item, S_IRUGO, get_##type##0##item, NULL);
+
+
+#define sysfs_vid(n) \
+sysfs_ro_n(cpu, n, _vid, GL520_REG_VID_INPUT)
+
+#define device_create_file_vid(client, n) \
+device_create_file(&client->dev, &dev_attr_cpu##n##_vid)
+
+#define sysfs_in(n) \
+sysfs_ro_n(in, n, _input, GL520_REG_IN##n##INPUT) \
+sysfs_rw_n(in, n, _min, GL520_REG_IN##n##_MIN) \
+sysfs_rw_n(in, n, _max, GL520_REG_IN##n##_MAX) \
+
+#define device_create_file_in(client, n) \
+({device_create_file(&client->dev, &dev_attr_in##n##_input); \
+device_create_file(&client->dev, &dev_attr_in##n##_min); \
+device_create_file(&client->dev, &dev_attr_in##n##_max);})
+
+#define sysfs_fan(n) \
+sysfs_ro_n(fan, n, _input, GL520_REG_FAN_INPUT) \
+sysfs_rw_n(fan, n, _min, GL520_REG_FAN_MIN) \
+sysfs_rw_n(fan, n, _div, GL520_REG_FAN_DIV)
+
+#define device_create_file_fan(client, n) \
+({device_create_file(&client->dev, &dev_attr_fan##n##_input); \
+device_create_file(&client->dev, &dev_attr_fan##n##_min); \
+device_create_file(&client->dev, &dev_attr_fan##n##_div);})
+
+#define sysfs_fan_off(n) \
+sysfs_rw_n(fan, n, _off, GL520_REG_FAN_OFF) \
+
+#define device_create_file_fan_off(client, n) \
+device_create_file(&client->dev, &dev_attr_fan##n##_off)
+
+#define sysfs_temp(n) \
+sysfs_ro_n(temp, n, _input, GL520_REG_TEMP##n##_INPUT) \
+sysfs_rw_n(temp, n, _max, GL520_REG_TEMP##n##_MAX) \
+sysfs_rw_n(temp, n, _max_hyst, GL520_REG_TEMP##n##_MAX_HYST)
+
+#define device_create_file_temp(client, n) \
+({device_create_file(&client->dev, &dev_attr_temp##n##_input); \
+device_create_file(&client->dev, &dev_attr_temp##n##_max); \
+device_create_file(&client->dev, &dev_attr_temp##n##_max_hyst);})
+
+#define sysfs_alarms() \
+sysfs_ro(alarms, , GL520_REG_ALARMS) \
+sysfs_rw(beep_enable, , GL520_REG_BEEP_ENABLE) \
+sysfs_rw(beep_mask, , GL520_REG_BEEP_MASK)
+
+#define device_create_file_alarms(client) \
+({device_create_file(&client->dev, &dev_attr_alarms); \
+device_create_file(&client->dev, &dev_attr_beep_enable); \
+device_create_file(&client->dev, &dev_attr_beep_mask);})
+
+
+sysfs_vid(0)
+
+sysfs_in(0)
+sysfs_in(1)
+sysfs_in(2)
+sysfs_in(3)
+sysfs_in(4)
+
+sysfs_fan(1)
+sysfs_fan(2)
+sysfs_fan_off(1)
+
+sysfs_temp(1)
+sysfs_temp(2)
+
+sysfs_alarms()
+
+
+static ssize_t get_cpu_vid(struct gl520_data *data, char *buf, int n)
+{
+       return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
+}
+
+#define VDD_FROM_REG(val) (((val)*95+2)/4)
+#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*4+47)/95),0,255))
+
+#define IN_FROM_REG(val) ((val)*19)
+#define IN_TO_REG(val) (SENSORS_LIMIT((((val)+9)/19),0,255))
+
+static ssize_t get_in_input(struct gl520_data *data, char *buf, int n)
+{
+       u8 r = data->in_input[n];
+
+       if (n == 0)
+               return sprintf(buf, "%d\n", VDD_FROM_REG(r));
+       else
+               return sprintf(buf, "%d\n", IN_FROM_REG(r));
+}
+
+static ssize_t get_in_min(struct gl520_data *data, char *buf, int n)
+{
+       u8 r = data->in_min[n];
+
+       if (n == 0)
+               return sprintf(buf, "%d\n", VDD_FROM_REG(r));
+       else
+               return sprintf(buf, "%d\n", IN_FROM_REG(r));
+}
+
+static ssize_t get_in_max(struct gl520_data *data, char *buf, int n)
+{
+       u8 r = data->in_max[n];
+
+       if (n == 0)
+               return sprintf(buf, "%d\n", VDD_FROM_REG(r));
+       else
+               return sprintf(buf, "%d\n", IN_FROM_REG(r));
+}
+
+static ssize_t set_in_min(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
+{
+       long v = simple_strtol(buf, NULL, 10);
+       u8 r;
+
+       down(&data->update_lock);
+
+       if (n == 0)
+               r = VDD_TO_REG(v);
+       else
+               r = IN_TO_REG(v);
+
+       data->in_min[n] = r;
+
+       if (n < 4)
+               gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff) | r);
+       else
+               gl520_write_value(client, reg, r);
+
+       up(&data->update_lock);
+       return count;
+}
+
+static ssize_t set_in_max(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
+{
+       long v = simple_strtol(buf, NULL, 10);
+       u8 r;
+
+       if (n == 0)
+               r = VDD_TO_REG(v);
+       else
+               r = IN_TO_REG(v);
+
+       down(&data->update_lock);
+
+       data->in_max[n] = r;
+
+       if (n < 4)
+               gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff00) | (r << 8));
+       else
+               gl520_write_value(client, reg, r);
+
+       up(&data->update_lock);
+       return count;
+}
+
+#define DIV_FROM_REG(val) (1 << (val))
+#define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (480000/((val) << (div))))
+#define FAN_TO_REG(val,div) ((val)<=0?0:SENSORS_LIMIT((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255));
+
+static ssize_t get_fan_input(struct gl520_data *data, char *buf, int n)
+{
+       return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_input[n - 1], data->fan_div[n - 1]));
+}
+
+static ssize_t get_fan_min(struct gl520_data *data, char *buf, int n)
+{
+       return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[n - 1], data->fan_div[n - 1]));
+}
+
+static ssize_t get_fan_div(struct gl520_data *data, char *buf, int n)
+{
+       return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[n - 1]));
+}
+
+static ssize_t get_fan_off(struct gl520_data *data, char *buf, int n)
+{
+       return sprintf(buf, "%d\n", data->fan_off);
+}
+
+static ssize_t set_fan_min(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
+{
+       unsigned long v = simple_strtoul(buf, NULL, 10);
+       u8 r;
+
+       down(&data->update_lock);
+       r = FAN_TO_REG(v, data->fan_div[n - 1]);
+       data->fan_min[n - 1] = r;
+
+       if (n == 1)
+               gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff00) | (r << 8));
+       else
+               gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff) | r);
+
+       data->beep_mask = gl520_read_value(client, GL520_REG_BEEP_MASK);
+       if (data->fan_min[n - 1] == 0)
+               data->alarm_mask &= (n == 1) ? ~0x20 : ~0x40;
+       else
+               data->alarm_mask |= (n == 1) ? 0x20 : 0x40;
+       data->beep_mask &= data->alarm_mask;
+       gl520_write_value(client, GL520_REG_BEEP_MASK, data->beep_mask);
+
+       up(&data->update_lock);
+       return count;
+}
+
+static ssize_t set_fan_div(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
+{
+       unsigned long v = simple_strtoul(buf, NULL, 10);
+       u8 r;
+
+       switch (v) {
+       case 1: r = 0; break;
+       case 2: r = 1; break;
+       case 4: r = 2; break;
+       case 8: r = 3; break;
+       default:
+               dev_err(&client->dev, "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", v);
+               return -EINVAL;
+       }
+
+       down(&data->update_lock);
+       data->fan_div[n - 1] = r;
+
+       if (n == 1)
+               gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xc0) | (r << 6));
+       else
+               gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x30) | (r << 4));
+
+       up(&data->update_lock);
+       return count;
+}
+
+static ssize_t set_fan_off(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
+{
+       u8 r = simple_strtoul(buf, NULL, 10)?1:0;
+
+       down(&data->update_lock);
+       data->fan_off = r;
+       gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x0c) | (r << 2));
+       up(&data->update_lock);
+       return count;
+}
+
+#define TEMP_FROM_REG(val) (((val) - 130) * 1000)
+#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-500:(val)+500) / 1000)+130),0,255))
+
+static ssize_t get_temp_input(struct gl520_data *data, char *buf, int n)
+{
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_input[n - 1]));
+}
+
+static ssize_t get_temp_max(struct gl520_data *data, char *buf, int n)
+{
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[n - 1]));
+}
+
+static ssize_t get_temp_max_hyst(struct gl520_data *data, char *buf, int n)
+{
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max_hyst[n - 1]));
+}
+
+static ssize_t set_temp_max(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
+{
+       long v = simple_strtol(buf, NULL, 10);
+
+       down(&data->update_lock);
+       data->temp_max[n - 1] = TEMP_TO_REG(v);;
+       gl520_write_value(client, reg, data->temp_max[n - 1]);
+       up(&data->update_lock);
+       return count;
+}
+
+static ssize_t set_temp_max_hyst(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
+{
+       long v = simple_strtol(buf, NULL, 10);
+
+       down(&data->update_lock);
+       data->temp_max_hyst[n - 1] = TEMP_TO_REG(v);
+       gl520_write_value(client, reg, data->temp_max_hyst[n - 1]);
+       up(&data->update_lock);
+       return count;
+}
+
+static ssize_t get_alarms(struct gl520_data *data, char *buf, int n)
+{
+       return sprintf(buf, "%d\n", data->alarms);
+}
+
+static ssize_t get_beep_enable(struct gl520_data *data, char *buf, int n)
+{
+       return sprintf(buf, "%d\n", data->beep_enable);
+}
+
+static ssize_t get_beep_mask(struct gl520_data *data, char *buf, int n)
+{
+       return sprintf(buf, "%d\n", data->beep_mask);
+}
+
+static ssize_t set_beep_enable(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
+{
+       u8 r = simple_strtoul(buf, NULL, 10)?0:1;
+
+       down(&data->update_lock);
+       data->beep_enable = !r;
+       gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x04) | (r << 2));
+       up(&data->update_lock);
+       return count;
+}
+
+static ssize_t set_beep_mask(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg)
+{
+       u8 r = simple_strtoul(buf, NULL, 10);
+       
+       down(&data->update_lock);
+       r &= data->alarm_mask;
+       data->beep_mask = r;
+       gl520_write_value(client, reg, r);
+       up(&data->update_lock);
+       return count;
+}
+
+
+/*
+ * Real code
+ */
+
+static int gl520_attach_adapter(struct i2c_adapter *adapter)
+{
+       if (!(adapter->class & I2C_CLASS_HWMON))
+               return 0;
+       return i2c_detect(adapter, &addr_data, gl520_detect);
+}
+
+static int gl520_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *new_client;
+       struct gl520_data *data;
+       int err = 0;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+                                    I2C_FUNC_SMBUS_WORD_DATA))
+               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 gl520_{read,write}_value. */
+
+       if (!(data = kmalloc(sizeof(struct gl520_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto exit;
+       }
+       memset(data, 0, sizeof(struct gl520_data));
+
+       new_client = &data->client;
+       i2c_set_clientdata(new_client, data);
+       new_client->addr = address;
+       new_client->adapter = adapter;
+       new_client->driver = &gl520_driver;
+       new_client->flags = 0;
+
+       /* Determine the chip type. */
+       if (kind < 0) {
+               if ((gl520_read_value(new_client, GL520_REG_CHIP_ID) != 0x20) ||
+                   ((gl520_read_value(new_client, GL520_REG_REVISION) & 0x7f) != 0x00) ||
+                   ((gl520_read_value(new_client, GL520_REG_CONF) & 0x80) != 0x00)) {
+                       dev_dbg(&new_client->dev, "Unknown chip type, skipping\n");
+                       goto exit_free;
+               }
+       }
+
+       /* Fill in the remaining client fields */
+       strlcpy(new_client->name, "gl520sm", 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 GL520SM chip */
+       gl520_init_client(new_client);
+
+       /* Register sysfs hooks */
+       device_create_file_vid(new_client, 0);
+
+       device_create_file_in(new_client, 0);
+       device_create_file_in(new_client, 1);
+       device_create_file_in(new_client, 2);
+       device_create_file_in(new_client, 3);
+       if (!data->two_temps)
+               device_create_file_in(new_client, 4);
+
+       device_create_file_fan(new_client, 1);
+       device_create_file_fan(new_client, 2);
+       device_create_file_fan_off(new_client, 1);
+
+       device_create_file_temp(new_client, 1);
+       if (data->two_temps)
+               device_create_file_temp(new_client, 2);
+
+       device_create_file_alarms(new_client);
+
+       return 0;
+
+exit_free:
+       kfree(data);
+exit:
+       return err;
+}
+
+
+/* Called when we have found a new GL520SM. */
+static void gl520_init_client(struct i2c_client *client)
+{
+       struct gl520_data *data = i2c_get_clientdata(client);
+       u8 oldconf, conf;
+
+       conf = oldconf = gl520_read_value(client, GL520_REG_CONF);
+
+       data->alarm_mask = 0xff;
+       data->vrm = i2c_which_vrm();
+
+       if (extra_sensor_type == 1)
+               conf &= ~0x10;
+       else if (extra_sensor_type == 2)
+               conf |= 0x10;
+       data->two_temps = !(conf & 0x10);
+
+       /* If IRQ# is disabled, we can safely force comparator mode */
+       if (!(conf & 0x20))
+               conf &= 0xf7;
+
+       /* Enable monitoring if needed */
+       conf |= 0x40;
+
+       if (conf != oldconf)
+               gl520_write_value(client, GL520_REG_CONF, conf);
+
+       gl520_update_device(&(client->dev));
+
+       if (data->fan_min[0] == 0)
+               data->alarm_mask &= ~0x20;
+       if (data->fan_min[1] == 0)
+               data->alarm_mask &= ~0x40;
+
+       data->beep_mask &= data->alarm_mask;
+       gl520_write_value(client, GL520_REG_BEEP_MASK, data->beep_mask);
+}
+
+static int gl520_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;
+}
+
+
+/* Registers 0x07 to 0x0c are word-sized, others are byte-sized 
+   GL520 uses a high-byte first convention */
+static int gl520_read_value(struct i2c_client *client, u8 reg)
+{
+       if ((reg >= 0x07) && (reg <= 0x0c))
+               return swab16(i2c_smbus_read_word_data(client, reg));
+       else
+               return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+       if ((reg >= 0x07) && (reg <= 0x0c))
+               return i2c_smbus_write_word_data(client, reg, swab16(value));
+       else
+               return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+
+static struct gl520_data *gl520_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct gl520_data *data = i2c_get_clientdata(client);
+       int val;
+
+       down(&data->update_lock);
+
+       if ((jiffies - data->last_updated > 2 * HZ) ||
+           (jiffies < data->last_updated) || !data->valid) {
+
+               dev_dbg(&client->dev, "Starting gl520sm update\n");
+
+               data->alarms = gl520_read_value(client, GL520_REG_ALARMS);
+               data->beep_mask = gl520_read_value(client, GL520_REG_BEEP_MASK);
+               data->vid = gl520_read_value(client, GL520_REG_VID_INPUT) & 0x1f;
+
+               val = gl520_read_value(client, GL520_REG_IN0_LIMIT);
+               data->in_min[0] = val & 0xff;
+               data->in_max[0] = (val >> 8) & 0xff;
+               val = gl520_read_value(client, GL520_REG_IN1_LIMIT);
+               data->in_min[1] = val & 0xff;
+               data->in_max[1] = (val >> 8) & 0xff;
+               val = gl520_read_value(client, GL520_REG_IN2_LIMIT);
+               data->in_min[2] = val & 0xff;
+               data->in_max[2] = (val >> 8) & 0xff;
+               val = gl520_read_value(client, GL520_REG_IN3_LIMIT);
+               data->in_min[3] = val & 0xff;
+               data->in_max[3] = (val >> 8) & 0xff;
+
+               val = gl520_read_value(client, GL520_REG_FAN_INPUT);
+               data->fan_input[0] = (val >> 8) & 0xff;
+               data->fan_input[1] = val & 0xff;
+
+               val = gl520_read_value(client, GL520_REG_FAN_MIN);
+               data->fan_min[0] = (val >> 8) & 0xff;
+               data->fan_min[1] = val & 0xff;
+
+               data->temp_input[0] = gl520_read_value(client, GL520_REG_TEMP1_INPUT);
+               data->temp_max[0] = gl520_read_value(client, GL520_REG_TEMP1_MAX);
+               data->temp_max_hyst[0] = gl520_read_value(client, GL520_REG_TEMP1_MAX_HYST);
+
+               val = gl520_read_value(client, GL520_REG_FAN_DIV);
+               data->fan_div[0] = (val >> 6) & 0x03;
+               data->fan_div[1] = (val >> 4) & 0x03;
+               data->fan_off = (val >> 2) & 0x01;
+
+               data->alarms &= data->alarm_mask;
+
+               val = gl520_read_value(client, GL520_REG_CONF);
+               data->beep_enable = !((val >> 2) & 1);
+
+               data->in_input[0] = gl520_read_value(client, GL520_REG_IN0_INPUT);
+               data->in_input[1] = gl520_read_value(client, GL520_REG_IN1_INPUT);
+               data->in_input[2] = gl520_read_value(client, GL520_REG_IN2_INPUT);
+               data->in_input[3] = gl520_read_value(client, GL520_REG_IN3_INPUT);
+
+               /* Temp1 and Vin4 are the same input */
+               if (data->two_temps) {
+                       data->temp_input[1] = gl520_read_value(client, GL520_REG_TEMP2_INPUT);
+                       data->temp_max[1] = gl520_read_value(client, GL520_REG_TEMP2_MAX);
+                       data->temp_max_hyst[1] = gl520_read_value(client, GL520_REG_TEMP2_MAX_HYST);
+               } else {
+                       data->in_input[4] = gl520_read_value(client, GL520_REG_IN4_INPUT);
+                       data->in_min[4] = gl520_read_value(client, GL520_REG_IN4_MIN);
+                       data->in_max[4] = gl520_read_value(client, GL520_REG_IN4_MAX);
+               }
+
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       up(&data->update_lock);
+
+       return data;
+}
+
+
+static int __init sensors_gl520sm_init(void)
+{
+       return i2c_add_driver(&gl520_driver);
+}
+
+static void __exit sensors_gl520sm_exit(void)
+{
+       i2c_del_driver(&gl520_driver);
+}
+
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
+       "Kyösti Mälkki <kmalkki@cc.hut.fi>, "
+       "Maarten Deprez <maartendeprez@users.sourceforge.net>");
+MODULE_DESCRIPTION("GL520SM driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_gl520sm_init);
+module_exit(sensors_gl520sm_exit);
diff --git a/drivers/i2c/chips/lm92.c b/drivers/i2c/chips/lm92.c
new file mode 100644 (file)
index 0000000..fe6e83d
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+ * lm92 - Hardware monitoring driver
+ * Copyright (C) 2005  Jean Delvare <khali@linux-fr.org>
+ *
+ * Based on the lm90 driver, with some ideas taken from the lm_sensors
+ * lm92 driver as well.
+ *
+ * The LM92 is a sensor chip made by National Semiconductor. It reports
+ * its own temperature with a 0.0625 deg resolution and a 0.33 deg
+ * accuracy. Complete datasheet can be obtained from National's website
+ * at:
+ *   http://www.national.com/pf/LM/LM92.html
+ *
+ * This driver also supports the MAX6635 sensor chip made by Maxim.
+ * This chip is compatible with the LM92, but has a lesser accuracy
+ * (1.0 deg). Complete datasheet can be obtained from Maxim's website
+ * at:
+ *   http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3074
+ *
+ * Since the LM92 was the first chipset supported by this driver, most
+ * comments will refer to this chipset, but are actually general and
+ * concern all supported chipsets, unless mentioned otherwise.
+ *
+ * Support could easily be added for the National Semiconductor LM76
+ * and Maxim MAX6633 and MAX6634 chips, which are mostly compatible
+ * with the LM92.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+
+
+/* The LM92 and MAX6635 have 2 two-state pins for address selection,
+   resulting in 4 possible addresses. */
+static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
+                                      I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(lm92);
+
+/* The LM92 registers */
+#define LM92_REG_CONFIG                        0x01 /* 8-bit, RW */
+#define LM92_REG_TEMP                  0x00 /* 16-bit, RO */
+#define LM92_REG_TEMP_HYST             0x02 /* 16-bit, RW */
+#define LM92_REG_TEMP_CRIT             0x03 /* 16-bit, RW */
+#define LM92_REG_TEMP_LOW              0x04 /* 16-bit, RW */
+#define LM92_REG_TEMP_HIGH             0x05 /* 16-bit, RW */
+#define LM92_REG_MAN_ID                        0x07 /* 16-bit, RO, LM92 only */
+
+/* The LM92 uses signed 13-bit values with LSB = 0.0625 degree Celsius,
+   left-justified in 16-bit registers. No rounding is done, with such
+   a resolution it's just not worth it. Note that the MAX6635 doesn't
+   make use of the 4 lower bits for limits (i.e. effective resolution
+   for limits is 1 degree Celsius). */
+static inline int TEMP_FROM_REG(s16 reg)
+{
+       return reg / 8 * 625 / 10;
+}
+
+static inline s16 TEMP_TO_REG(int val)
+{
+       if (val <= -60000)
+               return -60000 * 10 / 625 * 8;
+       if (val >= 160000)
+               return 160000 * 10 / 625 * 8;
+       return val * 10 / 625 * 8;
+}
+
+/* Alarm flags are stored in the 3 LSB of the temperature register */
+static inline u8 ALARMS_FROM_REG(s16 reg)
+{
+       return reg & 0x0007;
+}
+
+/* Driver data (common to all clients) */
+static struct i2c_driver lm92_driver;
+
+/* Client data (each client gets its own) */
+struct lm92_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 */
+       s16 temp1_input, temp1_crit, temp1_min, temp1_max, temp1_hyst;
+};
+
+
+/*
+ * Sysfs attributes and callback functions
+ */
+
+static struct lm92_data *lm92_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm92_data *data = i2c_get_clientdata(client);
+
+       down(&data->update_lock);
+
+       if (time_after(jiffies, data->last_updated + HZ)
+        || !data->valid) {
+               dev_dbg(&client->dev, "Updating lm92 data\n");
+               data->temp1_input = swab16(i2c_smbus_read_word_data(client,
+                                   LM92_REG_TEMP));
+               data->temp1_hyst = swab16(i2c_smbus_read_word_data(client,
+                                   LM92_REG_TEMP_HYST));
+               data->temp1_crit = swab16(i2c_smbus_read_word_data(client,
+                                   LM92_REG_TEMP_CRIT));
+               data->temp1_min = swab16(i2c_smbus_read_word_data(client,
+                                   LM92_REG_TEMP_LOW));
+               data->temp1_max = swab16(i2c_smbus_read_word_data(client,
+                                   LM92_REG_TEMP_HIGH));
+
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       up(&data->update_lock);
+
+       return data;
+}
+
+#define show_temp(value) \
+static ssize_t show_##value(struct device *dev, char *buf) \
+{ \
+       struct lm92_data *data = lm92_update_device(dev); \
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \
+}
+show_temp(temp1_input);
+show_temp(temp1_crit);
+show_temp(temp1_min);
+show_temp(temp1_max);
+
+#define set_temp(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 lm92_data *data = i2c_get_clientdata(client); \
+       long val = simple_strtol(buf, NULL, 10); \
+ \
+       down(&data->update_lock); \
+       data->value = TEMP_TO_REG(val); \
+       i2c_smbus_write_word_data(client, reg, swab16(data->value)); \
+       up(&data->update_lock); \
+       return count; \
+}
+set_temp(temp1_crit, LM92_REG_TEMP_CRIT);
+set_temp(temp1_min, LM92_REG_TEMP_LOW);
+set_temp(temp1_max, LM92_REG_TEMP_HIGH);
+
+static ssize_t show_temp1_crit_hyst(struct device *dev, char *buf)
+{
+       struct lm92_data *data = lm92_update_device(dev);
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_crit)
+                      - TEMP_FROM_REG(data->temp1_hyst));
+}
+static ssize_t show_temp1_max_hyst(struct device *dev, char *buf)
+{
+       struct lm92_data *data = lm92_update_device(dev);
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_max)
+                      - TEMP_FROM_REG(data->temp1_hyst));
+}
+static ssize_t show_temp1_min_hyst(struct device *dev, char *buf)
+{
+       struct lm92_data *data = lm92_update_device(dev);
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_min)
+                      + TEMP_FROM_REG(data->temp1_hyst));
+}
+
+static ssize_t set_temp1_crit_hyst(struct device *dev, const char *buf,
+       size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm92_data *data = i2c_get_clientdata(client);
+       long val = simple_strtol(buf, NULL, 10);
+
+       down(&data->update_lock);
+       data->temp1_hyst = TEMP_FROM_REG(data->temp1_crit) - val;
+       i2c_smbus_write_word_data(client, LM92_REG_TEMP_HYST,
+                                 swab16(TEMP_TO_REG(data->temp1_hyst)));
+       up(&data->update_lock);
+       return count;
+}
+
+static ssize_t show_alarms(struct device *dev, char *buf)
+{
+       struct lm92_data *data = lm92_update_device(dev);
+       return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp1_input));
+}
+
+static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1_input, NULL);
+static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp1_crit,
+       set_temp1_crit);
+static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp1_crit_hyst,
+       set_temp1_crit_hyst);
+static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp1_min,
+       set_temp1_min);
+static DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_temp1_min_hyst, NULL);
+static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp1_max,
+       set_temp1_max);
+static DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp1_max_hyst, NULL);
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+
+
+/*
+ * Detection and registration
+ */
+
+static void lm92_init_client(struct i2c_client *client)
+{
+       u8 config;
+
+       /* Start the conversions if needed */
+       config = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG);
+       if (config & 0x01)
+               i2c_smbus_write_byte_data(client, LM92_REG_CONFIG,
+                                         config & 0xFE);
+}
+
+/* The MAX6635 has no identification register, so we have to use tricks
+   to identify it reliably. This is somewhat slow.
+   Note that we do NOT rely on the 2 MSB of the configuration register
+   always reading 0, as suggested by the datasheet, because it was once
+   reported not to be true. */
+static int max6635_check(struct i2c_client *client)
+{
+       u16 temp_low, temp_high, temp_hyst, temp_crit;
+       u8 conf;
+       int i;
+
+       /* No manufacturer ID register, so a read from this address will
+          always return the last read value. */
+       temp_low = i2c_smbus_read_word_data(client, LM92_REG_TEMP_LOW);
+       if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_low)
+               return 0;
+       temp_high = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HIGH);
+       if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_high)
+               return 0;
+       
+       /* Limits are stored as integer values (signed, 9-bit). */
+       if ((temp_low & 0x7f00) || (temp_high & 0x7f00))
+               return 0;
+       temp_hyst = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HYST);
+       temp_crit = i2c_smbus_read_word_data(client, LM92_REG_TEMP_CRIT);
+       if ((temp_hyst & 0x7f00) || (temp_crit & 0x7f00))
+               return 0;
+
+       /* Registers addresses were found to cycle over 16-byte boundaries.
+          We don't test all registers with all offsets so as to save some
+          reads and time, but this should still be sufficient to dismiss
+          non-MAX6635 chips. */
+       conf = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG);
+       for (i=16; i<96; i*=2) {
+               if (temp_hyst != i2c_smbus_read_word_data(client,
+                                LM92_REG_TEMP_HYST + i - 16)
+                || temp_crit != i2c_smbus_read_word_data(client,
+                                LM92_REG_TEMP_CRIT + i)
+                || temp_low != i2c_smbus_read_word_data(client,
+                               LM92_REG_TEMP_LOW + i + 16)
+                || temp_high != i2c_smbus_read_word_data(client,
+                                LM92_REG_TEMP_HIGH + i + 32)
+                || conf != i2c_smbus_read_byte_data(client,
+                           LM92_REG_CONFIG + i))
+                       return 0;
+       }
+
+       return 1;
+}
+
+/* The following function does more than just detection. If detection
+   succeeds, it also registers the new chip. */
+static int lm92_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *new_client;
+       struct lm92_data *data;
+       int err = 0;
+       char *name;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
+                                           | I2C_FUNC_SMBUS_WORD_DATA))
+               goto exit;
+
+       if (!(data = kmalloc(sizeof(struct lm92_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto exit;
+       }
+       memset(data, 0, sizeof(struct lm92_data));
+
+       /* Fill in enough client fields so that we can read from the chip,
+          which is required for identication */
+       new_client = &data->client;
+       i2c_set_clientdata(new_client, data);
+       new_client->addr = address;
+       new_client->adapter = adapter;
+       new_client->driver = &lm92_driver;
+       new_client->flags = 0;
+
+       /* A negative kind means that the driver was loaded with no force
+          parameter (default), so we must identify the chip. */
+       if (kind < 0) {
+               u8 config = i2c_smbus_read_byte_data(new_client,
+                            LM92_REG_CONFIG);
+               u16 man_id = i2c_smbus_read_word_data(new_client,
+                            LM92_REG_MAN_ID);
+
+               if ((config & 0xe0) == 0x00
+                && man_id == 0x0180) {
+                       pr_info("lm92: Found National Semiconductor LM92 chip\n");
+                       kind = lm92;
+               } else
+               if (max6635_check(new_client)) {
+                       pr_info("lm92: Found Maxim MAX6635 chip\n");
+                       kind = lm92; /* No separate prefix */
+               }
+               else
+                       goto exit_free;
+       } else
+       if (kind == 0) /* Default to an LM92 if forced */
+               kind = lm92;
+
+       /* Give it the proper name */
+       if (kind == lm92) {
+               name = "lm92";
+       } else { /* Supposedly cannot happen */
+               dev_dbg(&new_client->dev, "Kind out of range?\n");
+               goto exit_free;
+       }
+
+       /* Fill in the remaining client fields */
+       strlcpy(new_client->name, name, I2C_NAME_SIZE);
+       data->valid = 0;
+       init_MUTEX(&data->update_lock);
+
+       /* Tell the i2c subsystem a new client has arrived */
+       if ((err = i2c_attach_client(new_client)))
+               goto exit_free;
+
+       /* Initialize the chipset */
+       lm92_init_client(new_client);
+
+       /* Register sysfs hooks */
+       device_create_file(&new_client->dev, &dev_attr_temp1_input);
+       device_create_file(&new_client->dev, &dev_attr_temp1_crit);
+       device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
+       device_create_file(&new_client->dev, &dev_attr_temp1_min);
+       device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst);
+       device_create_file(&new_client->dev, &dev_attr_temp1_max);
+       device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
+       device_create_file(&new_client->dev, &dev_attr_alarms);
+
+       return 0;
+
+exit_free:
+       kfree(data);
+exit:
+       return err;
+}
+
+static int lm92_attach_adapter(struct i2c_adapter *adapter)
+{
+       if (!(adapter->class & I2C_CLASS_HWMON))
+               return 0;
+       return i2c_detect(adapter, &addr_data, lm92_detect);
+}
+
+static int lm92_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;
+}
+
+
+/*
+ * Module and driver stuff
+ */
+
+static struct i2c_driver lm92_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "lm92",
+       .id             = I2C_DRIVERID_LM92,
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = lm92_attach_adapter,
+       .detach_client  = lm92_detach_client,
+};
+
+static int __init sensors_lm92_init(void)
+{
+       return i2c_add_driver(&lm92_driver);
+}
+
+static void __exit sensors_lm92_exit(void)
+{
+       i2c_del_driver(&lm92_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("LM92/MAX6635 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_lm92_init);
+module_exit(sensors_lm92_exit);
diff --git a/drivers/i2c/chips/m41t00.c b/drivers/i2c/chips/m41t00.c
new file mode 100644 (file)
index 0000000..e771566
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * drivers/i2c/chips/m41t00.c
+ *
+ * I2C client/driver for the ST M41T00 Real-Time Clock chip.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2005 (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 i2c client/driver wedges between the drivers/char/genrtc.c RTC
+ * interface and the SMBus interface of the i2c subsystem.
+ * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
+ * recommened in .../Documentation/i2c/writing-clients section
+ * "Sending and receiving", using SMBus level communication is preferred.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+
+#include <asm/time.h>
+#include <asm/rtc.h>
+
+#define        M41T00_DRV_NAME         "m41t00"
+
+static DECLARE_MUTEX(m41t00_mutex);
+
+static struct i2c_driver m41t00_driver;
+static struct i2c_client *save_client;
+
+static unsigned short ignore[] = { I2C_CLIENT_END };
+static unsigned short normal_addr[] = { 0x68, I2C_CLIENT_END };
+
+static struct i2c_client_address_data addr_data = {
+       .normal_i2c             = normal_addr,
+       .normal_i2c_range       = ignore,
+       .probe                  = ignore,
+       .probe_range            = ignore,
+       .ignore                 = ignore,
+       .ignore_range           = ignore,
+       .force                  = ignore,
+};
+
+ulong
+m41t00_get_rtc_time(void)
+{
+       s32     sec, min, hour, day, mon, year;
+       s32     sec1, min1, hour1, day1, mon1, year1;
+       ulong   limit = 10;
+
+       sec = min = hour = day = mon = year = 0;
+       sec1 = min1 = hour1 = day1 = mon1 = year1 = 0;
+
+       down(&m41t00_mutex);
+       do {
+               if (((sec = i2c_smbus_read_byte_data(save_client, 0)) >= 0)
+                       && ((min = i2c_smbus_read_byte_data(save_client, 1))
+                               >= 0)
+                       && ((hour = i2c_smbus_read_byte_data(save_client, 2))
+                               >= 0)
+                       && ((day = i2c_smbus_read_byte_data(save_client, 4))
+                               >= 0)
+                       && ((mon = i2c_smbus_read_byte_data(save_client, 5))
+                               >= 0)
+                       && ((year = i2c_smbus_read_byte_data(save_client, 6))
+                               >= 0)
+                       && ((sec == sec1) && (min == min1) && (hour == hour1)
+                               && (day == day1) && (mon == mon1)
+                               && (year == year1)))
+
+                               break;
+
+               sec1 = sec;
+               min1 = min;
+               hour1 = hour;
+               day1 = day;
+               mon1 = mon;
+               year1 = year;
+       } while (--limit > 0);
+       up(&m41t00_mutex);
+
+       if (limit == 0) {
+               dev_warn(&save_client->dev,
+                       "m41t00: can't read rtc chip\n");
+               sec = min = hour = day = mon = year = 0;
+       }
+
+       sec &= 0x7f;
+       min &= 0x7f;
+       hour &= 0x3f;
+       day &= 0x3f;
+       mon &= 0x1f;
+       year &= 0xff;
+
+       BCD_TO_BIN(sec);
+       BCD_TO_BIN(min);
+       BCD_TO_BIN(hour);
+       BCD_TO_BIN(day);
+       BCD_TO_BIN(mon);
+       BCD_TO_BIN(year);
+
+       year += 1900;
+       if (year < 1970)
+               year += 100;
+
+       return mktime(year, mon, day, hour, min, sec);
+}
+
+static void
+m41t00_set_tlet(ulong arg)
+{
+       struct rtc_time tm;
+       ulong   nowtime = *(ulong *)arg;
+
+       to_tm(nowtime, &tm);
+       tm.tm_year = (tm.tm_year - 1900) % 100;
+
+       BIN_TO_BCD(tm.tm_sec);
+       BIN_TO_BCD(tm.tm_min);
+       BIN_TO_BCD(tm.tm_hour);
+       BIN_TO_BCD(tm.tm_mon);
+       BIN_TO_BCD(tm.tm_mday);
+       BIN_TO_BCD(tm.tm_year);
+
+       down(&m41t00_mutex);
+       if ((i2c_smbus_write_byte_data(save_client, 0, tm.tm_sec & 0x7f) < 0)
+               || (i2c_smbus_write_byte_data(save_client, 1, tm.tm_min & 0x7f)
+                       < 0)
+               || (i2c_smbus_write_byte_data(save_client, 2, tm.tm_hour & 0x7f)
+                       < 0)
+               || (i2c_smbus_write_byte_data(save_client, 4, tm.tm_mday & 0x7f)
+                       < 0)
+               || (i2c_smbus_write_byte_data(save_client, 5, tm.tm_mon & 0x7f)
+                       < 0)
+               || (i2c_smbus_write_byte_data(save_client, 6, tm.tm_year & 0x7f)
+                       < 0))
+
+               dev_warn(&save_client->dev,"m41t00: can't write to rtc chip\n");
+
+       up(&m41t00_mutex);
+       return;
+}
+
+ulong  new_time;
+
+DECLARE_TASKLET_DISABLED(m41t00_tasklet, m41t00_set_tlet, (ulong)&new_time);
+
+int
+m41t00_set_rtc_time(ulong nowtime)
+{
+       new_time = nowtime;
+
+       if (in_interrupt())
+               tasklet_schedule(&m41t00_tasklet);
+       else
+               m41t00_set_tlet((ulong)&new_time);
+
+       return 0;
+}
+
+/*
+ *****************************************************************************
+ *
+ *     Driver Interface
+ *
+ *****************************************************************************
+ */
+static int
+m41t00_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+       struct i2c_client *client;
+       int rc;
+
+       client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+       if (!client)
+               return -ENOMEM;
+
+       memset(client, 0, sizeof(struct i2c_client));
+       strncpy(client->name, M41T00_DRV_NAME, I2C_NAME_SIZE);
+       client->flags = I2C_DF_NOTIFY;
+       client->addr = addr;
+       client->adapter = adap;
+       client->driver = &m41t00_driver;
+
+       if ((rc = i2c_attach_client(client)) != 0) {
+               kfree(client);
+               return rc;
+       }
+
+       save_client = client;
+       return 0;
+}
+
+static int
+m41t00_attach(struct i2c_adapter *adap)
+{
+       return i2c_probe(adap, &addr_data, m41t00_probe);
+}
+
+static int
+m41t00_detach(struct i2c_client *client)
+{
+       int     rc;
+
+       if ((rc = i2c_detach_client(client)) == 0) {
+               kfree(i2c_get_clientdata(client));
+               tasklet_kill(&m41t00_tasklet);
+       }
+       return rc;
+}
+
+static struct i2c_driver m41t00_driver = {
+       .owner          = THIS_MODULE,
+       .name           = M41T00_DRV_NAME,
+       .id             = I2C_DRIVERID_STM41T00,
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = m41t00_attach,
+       .detach_client  = m41t00_detach,
+};
+
+static int __init
+m41t00_init(void)
+{
+       return i2c_add_driver(&m41t00_driver);
+}
+
+static void __exit
+m41t00_exit(void)
+{
+       i2c_del_driver(&m41t00_driver);
+       return;
+}
+
+module_init(m41t00_init);
+module_exit(m41t00_exit);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("ST Microelectronics M41T00 RTC I2C Client Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/chips/sis5595.c b/drivers/i2c/chips/sis5595.c
new file mode 100644 (file)
index 0000000..7ea8453
--- /dev/null
@@ -0,0 +1,816 @@
+/*
+    sis5595.c - Part of lm_sensors, Linux kernel modules
+               for hardware monitoring
+
+    Copyright (C) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>,
+                       Kyösti Mälkki <kmalkki@cc.hut.fi>, and
+                       Mark D. Studebaker <mdsxyz123@yahoo.com>
+    Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with
+    the help of 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.
+*/
+
+/*
+   SiS southbridge has a LM78-like chip integrated on the same IC.
+   This driver is a customized copy of lm78.c
+   
+   Supports following revisions:
+       Version         PCI ID          PCI Revision
+       1               1039/0008       AF or less
+       2               1039/0008       B0 or greater
+
+   Note: these chips contain a 0008 device which is incompatible with the
+        5595. We recognize these by the presence of the listed
+        "blacklist" PCI ID and refuse to load.
+
+   NOT SUPPORTED       PCI ID          BLACKLIST PCI ID        
+        540            0008            0540
+        550            0008            0550
+       5513            0008            5511
+       5581            0008            5597
+       5582            0008            5597
+       5597            0008            5597
+       5598            0008            5597/5598
+        630            0008            0630
+        645            0008            0645
+        730            0008            0730
+        735            0008            0735
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+
+/* If force_addr is set to anything different from 0, we forcibly enable
+   the device at the given address. */
+static u16 force_addr;
+module_param(force_addr, ushort, 0);
+MODULE_PARM_DESC(force_addr,
+                "Initialize the base address of the sensors");
+
+/* Addresses to scan.
+   Note that we can't determine the ISA address until we have initialized
+   our module */
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(sis5595);
+
+/* Many SIS5595 constants specified below */
+
+/* Length of ISA address segment */
+#define SIS5595_EXTENT 8
+/* PCI Config Registers */
+#define SIS5595_REVISION_REG 0x08
+#define SIS5595_BASE_REG 0x68
+#define SIS5595_PIN_REG 0x7A
+#define SIS5595_ENABLE_REG 0x7B
+
+/* Where are the ISA address/data registers relative to the base address */
+#define SIS5595_ADDR_REG_OFFSET 5
+#define SIS5595_DATA_REG_OFFSET 6
+
+/* The SIS5595 registers */
+#define SIS5595_REG_IN_MAX(nr) (0x2b + (nr) * 2)
+#define SIS5595_REG_IN_MIN(nr) (0x2c + (nr) * 2)
+#define SIS5595_REG_IN(nr) (0x20 + (nr))
+
+#define SIS5595_REG_FAN_MIN(nr) (0x3b + (nr))
+#define SIS5595_REG_FAN(nr) (0x28 + (nr))
+
+/* On the first version of the chip, the temp registers are separate.
+   On the second version,
+   TEMP pin is shared with IN4, configured in PCI register 0x7A.
+   The registers are the same as well.
+   OVER and HYST are really MAX and MIN. */
+
+#define REV2MIN        0xb0
+#define SIS5595_REG_TEMP       (( data->revision) >= REV2MIN) ? \
+                                       SIS5595_REG_IN(4) : 0x27
+#define SIS5595_REG_TEMP_OVER  (( data->revision) >= REV2MIN) ? \
+                                       SIS5595_REG_IN_MAX(4) : 0x39
+#define SIS5595_REG_TEMP_HYST  (( data->revision) >= REV2MIN) ? \
+                                       SIS5595_REG_IN_MIN(4) : 0x3a
+
+#define SIS5595_REG_CONFIG 0x40
+#define SIS5595_REG_ALARM1 0x41
+#define SIS5595_REG_ALARM2 0x42
+#define SIS5595_REG_FANDIV 0x47
+
+/* Conversions. Limit checking is only done on the TO_REG
+   variants. */
+
+/* IN: mV, (0V to 4.08V)
+   REG: 16mV/bit */
+static inline u8 IN_TO_REG(unsigned long val)
+{
+       unsigned long nval = SENSORS_LIMIT(val, 0, 4080);
+       return (nval + 8) / 16;
+}
+#define IN_FROM_REG(val) ((val) *  16)
+
+static inline u8 FAN_TO_REG(long rpm, int div)
+{
+       if (rpm <= 0)
+               return 255;
+       return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
+}
+
+static inline int FAN_FROM_REG(u8 val, int div)
+{
+       return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div);
+}
+
+/* TEMP: mC (-54.12C to +157.53C)
+   REG: 0.83C/bit + 52.12, two's complement  */
+static inline int TEMP_FROM_REG(s8 val)
+{
+       return val * 830 + 52120;
+}
+static inline s8 TEMP_TO_REG(int val)
+{
+       int nval = SENSORS_LIMIT(val, -54120, 157530) ;
+       return nval<0 ? (nval-5212-415)/830 : (nval-5212+415)/830;
+}
+
+/* FAN DIV: 1, 2, 4, or 8 (defaults to 2)
+   REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */
+static inline u8 DIV_TO_REG(int val)
+{
+       return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1;
+}
+#define DIV_FROM_REG(val) (1 << (val))
+
+/* For the SIS5595, we need to keep some data in memory. That
+   data is pointed to by sis5595_list[NR]->data. The structure itself is
+   dynamically allocated, at the time when the new sis5595 client is
+   allocated. */
+struct sis5595_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 */
+       char maxins;            /* == 3 if temp enabled, otherwise == 4 */
+       u8 revision;            /* Reg. value */
+
+       u8 in[5];               /* Register value */
+       u8 in_max[5];           /* Register value */
+       u8 in_min[5];           /* Register value */
+       u8 fan[2];              /* Register value */
+       u8 fan_min[2];          /* Register value */
+       s8 temp;                /* Register value */
+       s8 temp_over;           /* Register value */
+       s8 temp_hyst;           /* Register value */
+       u8 fan_div[2];          /* Register encoding, shifted right */
+       u16 alarms;             /* Register encoding, combined */
+};
+
+static struct pci_dev *s_bridge;       /* pointer to the (only) sis5595 */
+
+static int sis5595_attach_adapter(struct i2c_adapter *adapter);
+static int sis5595_detect(struct i2c_adapter *adapter, int address, int kind);
+static int sis5595_detach_client(struct i2c_client *client);
+
+static int sis5595_read_value(struct i2c_client *client, u8 register);
+static int sis5595_write_value(struct i2c_client *client, u8 register, u8 value);
+static struct sis5595_data *sis5595_update_device(struct device *dev);
+static void sis5595_init_client(struct i2c_client *client);
+
+static struct i2c_driver sis5595_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "sis5595",
+       .id             = I2C_DRIVERID_SIS5595,
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = sis5595_attach_adapter,
+       .detach_client  = sis5595_detach_client,
+};
+
+/* 4 Voltages */
+static ssize_t show_in(struct device *dev, char *buf, int nr)
+{
+       struct sis5595_data *data = sis5595_update_device(dev);
+       return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr]));
+}
+
+static ssize_t show_in_min(struct device *dev, char *buf, int nr)
+{
+       struct sis5595_data *data = sis5595_update_device(dev);
+       return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr]));
+}
+
+static ssize_t show_in_max(struct device *dev, char *buf, int nr)
+{
+       struct sis5595_data *data = sis5595_update_device(dev);
+       return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[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 sis5595_data *data = i2c_get_clientdata(client);
+       unsigned long val = simple_strtoul(buf, NULL, 10);
+
+       down(&data->update_lock);
+       data->in_min[nr] = IN_TO_REG(val);
+       sis5595_write_value(client, SIS5595_REG_IN_MIN(nr), data->in_min[nr]);
+       up(&data->update_lock);
+       return count;
+}
+
+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 sis5595_data *data = i2c_get_clientdata(client);
+       unsigned long val = simple_strtoul(buf, NULL, 10);
+
+       down(&data->update_lock);
+       data->in_max[nr] = IN_TO_REG(val);
+       sis5595_write_value(client, SIS5595_REG_IN_MAX(nr), data->in_max[nr]);
+       up(&data->update_lock);
+       return count;
+}
+
+#define show_in_offset(offset)                                 \
+static ssize_t                                                 \
+       show_in##offset (struct device *dev, char *buf)         \
+{                                                              \
+       return show_in(dev, buf, offset);                       \
+}                                                              \
+static DEVICE_ATTR(in##offset##_input, S_IRUGO,                \
+               show_in##offset, NULL);                         \
+static ssize_t                                                 \
+       show_in##offset##_min (struct device *dev, char *buf)   \
+{                                                              \
+       return show_in_min(dev, buf, 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##_min (struct device *dev,       \
+               const char *buf, size_t count)                  \
+{                                                              \
+       return set_in_min(dev, buf, count, 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##_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);
+
+show_in_offset(0);
+show_in_offset(1);
+show_in_offset(2);
+show_in_offset(3);
+show_in_offset(4);
+
+/* Temperature */
+static ssize_t show_temp(struct device *dev, char *buf)
+{
+       struct sis5595_data *data = sis5595_update_device(dev);
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp));
+}
+
+static ssize_t show_temp_over(struct device *dev, char *buf)
+{
+       struct sis5595_data *data = sis5595_update_device(dev);
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over));
+}
+
+static ssize_t set_temp_over(struct device *dev, const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct sis5595_data *data = i2c_get_clientdata(client);
+       long val = simple_strtol(buf, NULL, 10);
+
+       down(&data->update_lock);
+       data->temp_over = TEMP_TO_REG(val);
+       sis5595_write_value(client, SIS5595_REG_TEMP_OVER, data->temp_over);
+       up(&data->update_lock);
+       return count;
+}
+
+static ssize_t show_temp_hyst(struct device *dev, char *buf)
+{
+       struct sis5595_data *data = sis5595_update_device(dev);
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst));
+}
+
+static ssize_t set_temp_hyst(struct device *dev, const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct sis5595_data *data = i2c_get_clientdata(client);
+       long val = simple_strtol(buf, NULL, 10);
+
+       down(&data->update_lock);
+       data->temp_hyst = TEMP_TO_REG(val);
+       sis5595_write_value(client, SIS5595_REG_TEMP_HYST, data->temp_hyst);
+       up(&data->update_lock);
+       return count;
+}
+
+static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL);
+static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
+               show_temp_over, set_temp_over);
+static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
+               show_temp_hyst, set_temp_hyst);
+
+/* 2 Fans */
+static ssize_t show_fan(struct device *dev, char *buf, int nr)
+{
+       struct sis5595_data *data = sis5595_update_device(dev);
+       return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
+               DIV_FROM_REG(data->fan_div[nr])) );
+}
+
+static ssize_t show_fan_min(struct device *dev, char *buf, int nr)
+{
+       struct sis5595_data *data = sis5595_update_device(dev);
+       return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr],
+               DIV_FROM_REG(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 sis5595_data *data = i2c_get_clientdata(client);
+       unsigned long val = simple_strtoul(buf, NULL, 10);
+
+       down(&data->update_lock);
+       data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
+       sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]);
+       up(&data->update_lock);
+       return count;
+}
+
+static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
+{
+       struct sis5595_data *data = sis5595_update_device(dev);
+       return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) );
+}
+
+/* Note: we save and restore the fan minimum here, because its value is
+   determined in part by the fan divisor.  This follows the principle of
+   least suprise; the user doesn't expect the fan minimum to change just
+   because the divisor 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 sis5595_data *data = i2c_get_clientdata(client);
+       unsigned long min;
+       unsigned long val = simple_strtoul(buf, NULL, 10);
+       int reg;
+
+       down(&data->update_lock);
+       min = FAN_FROM_REG(data->fan_min[nr],
+                       DIV_FROM_REG(data->fan_div[nr]));
+       reg = sis5595_read_value(client, SIS5595_REG_FANDIV);
+
+       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:
+               dev_err(&client->dev, "fan_div value %ld not "
+                       "supported. Choose one of 1, 2, 4 or 8!\n", val);
+               up(&data->update_lock);
+               return -EINVAL;
+       }
+       
+       switch (nr) {
+       case 0:
+               reg = (reg & 0xcf) | (data->fan_div[nr] << 4);
+               break;
+       case 1:
+               reg = (reg & 0x3f) | (data->fan_div[nr] << 6);
+               break;
+       }
+       sis5595_write_value(client, SIS5595_REG_FANDIV, reg);
+       data->fan_min[nr] =
+               FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
+       sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]);
+       up(&data->update_lock);
+       return count;
+}
+
+#define show_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 show_fan_##offset##_div (struct device *dev, char *buf) \
+{                                                                      \
+       return show_fan_div(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);
+
+show_fan_offset(1);
+show_fan_offset(2);
+
+static ssize_t set_fan_1_div(struct device *dev, const char *buf,
+               size_t count)
+{
+       return set_fan_div(dev, buf, count, 0) ;
+}
+
+static ssize_t set_fan_2_div(struct device *dev, const char *buf,
+               size_t count)
+{
+       return set_fan_div(dev, buf, count, 1) ;
+}
+static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
+               show_fan_1_div, set_fan_1_div);
+static DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR,
+               show_fan_2_div, set_fan_2_div);
+
+/* Alarms */
+static ssize_t show_alarms(struct device *dev, char *buf)
+{
+       struct sis5595_data *data = sis5595_update_device(dev);
+       return sprintf(buf, "%d\n", data->alarms);
+}
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+/* This is called when the module is loaded */
+static int sis5595_attach_adapter(struct i2c_adapter *adapter)
+{
+       if (!(adapter->class & I2C_CLASS_HWMON))
+               return 0;
+       return i2c_detect(adapter, &addr_data, sis5595_detect);
+}
+
+int sis5595_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+       int err = 0;
+       int i;
+       struct i2c_client *new_client;
+       struct sis5595_data *data;
+       char val;
+       u16 a;
+
+       /* Make sure we are probing the ISA bus!!  */
+       if (!i2c_is_isa_adapter(adapter))
+               goto exit;
+
+       if (force_addr)
+               address = force_addr & ~(SIS5595_EXTENT - 1);
+       /* Reserve the ISA region */
+       if (!request_region(address, SIS5595_EXTENT, sis5595_driver.name)) {
+               err = -EBUSY;
+               goto exit;
+       }
+       if (force_addr) {
+               dev_warn(&adapter->dev, "forcing ISA address 0x%04X\n", address);
+               if (PCIBIOS_SUCCESSFUL !=
+                   pci_write_config_word(s_bridge, SIS5595_BASE_REG, address))
+                       goto exit_release;
+               if (PCIBIOS_SUCCESSFUL !=
+                   pci_read_config_word(s_bridge, SIS5595_BASE_REG, &a))
+                       goto exit_release;
+               if ((a & ~(SIS5595_EXTENT - 1)) != address)
+                       /* doesn't work for some chips? */
+                       goto exit_release;
+       }
+
+       if (PCIBIOS_SUCCESSFUL !=
+           pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) {
+               goto exit_release;
+       }
+       if ((val & 0x80) == 0) {
+               if (PCIBIOS_SUCCESSFUL !=
+                   pci_write_config_byte(s_bridge, SIS5595_ENABLE_REG,
+                                         val | 0x80))
+                       goto exit_release;
+               if (PCIBIOS_SUCCESSFUL !=
+                   pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val))
+                       goto exit_release;
+               if ((val & 0x80) == 0) 
+                       /* doesn't work for some chips! */
+                       goto exit_release;
+       }
+
+       if (!(data = kmalloc(sizeof(struct sis5595_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto exit_release;
+       }
+       memset(data, 0, sizeof(struct sis5595_data));
+
+       new_client = &data->client;
+       new_client->addr = address;
+       init_MUTEX(&data->lock);
+       i2c_set_clientdata(new_client, data);
+       new_client->adapter = adapter;
+       new_client->driver = &sis5595_driver;
+       new_client->flags = 0;
+
+       /* Check revision and pin registers to determine whether 4 or 5 voltages */
+       pci_read_config_byte(s_bridge, SIS5595_REVISION_REG, &(data->revision));
+       /* 4 voltages, 1 temp */
+       data->maxins = 3;
+       if (data->revision >= REV2MIN) {
+               pci_read_config_byte(s_bridge, SIS5595_PIN_REG, &val);
+               if (!(val & 0x80))
+                       /* 5 voltages, no temps */
+                       data->maxins = 4;
+       }
+       
+       /* Fill in the remaining client fields and put it into the global list */
+       strlcpy(new_client->name, "sis5595", 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 SIS5595 chip */
+       sis5595_init_client(new_client);
+
+       /* A few vars need to be filled upon startup */
+       for (i = 0; i < 2; i++) {
+               data->fan_min[i] = sis5595_read_value(new_client,
+                                       SIS5595_REG_FAN_MIN(i));
+       }
+
+       /* Register sysfs hooks */
+       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_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);
+       if (data->maxins == 4) {
+               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);
+       }
+       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);
+       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_alarms);
+       if (data->maxins == 3) {
+               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_max_hyst);
+       }
+       return 0;
+       
+exit_free:
+       kfree(data);
+exit_release:
+       release_region(address, SIS5595_EXTENT);
+exit:
+       return err;
+}
+
+static int sis5595_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;
+       }
+
+       if (i2c_is_isa_client(client))
+               release_region(client->addr, SIS5595_EXTENT);
+
+       kfree(i2c_get_clientdata(client));
+
+       return 0;
+}
+
+
+/* ISA access must be locked explicitly. */
+static int sis5595_read_value(struct i2c_client *client, u8 reg)
+{
+       int res;
+
+       struct sis5595_data *data = i2c_get_clientdata(client);
+       down(&data->lock);
+       outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET);
+       res = inb_p(client->addr + SIS5595_DATA_REG_OFFSET);
+       up(&data->lock);
+       return res;
+}
+
+static int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value)
+{
+       struct sis5595_data *data = i2c_get_clientdata(client);
+       down(&data->lock);
+       outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET);
+       outb_p(value, client->addr + SIS5595_DATA_REG_OFFSET);
+       up(&data->lock);
+       return 0;
+}
+
+/* Called when we have found a new SIS5595. */
+static void sis5595_init_client(struct i2c_client *client)
+{
+       u8 config = sis5595_read_value(client, SIS5595_REG_CONFIG);
+       if (!(config & 0x01))
+               sis5595_write_value(client, SIS5595_REG_CONFIG,
+                               (config & 0xf7) | 0x01);
+}
+
+static struct sis5595_data *sis5595_update_device(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct sis5595_data *data = i2c_get_clientdata(client);
+       int i;
+
+       down(&data->update_lock);
+
+       if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+           || !data->valid) {
+
+               for (i = 0; i <= data->maxins; i++) {
+                       data->in[i] =
+                           sis5595_read_value(client, SIS5595_REG_IN(i));
+                       data->in_min[i] =
+                           sis5595_read_value(client,
+                                              SIS5595_REG_IN_MIN(i));
+                       data->in_max[i] =
+                           sis5595_read_value(client,
+                                              SIS5595_REG_IN_MAX(i));
+               }
+               for (i = 0; i < 2; i++) {
+                       data->fan[i] =
+                           sis5595_read_value(client, SIS5595_REG_FAN(i));
+                       data->fan_min[i] =
+                           sis5595_read_value(client,
+                                              SIS5595_REG_FAN_MIN(i));
+               }
+               if (data->maxins == 3) {
+                       data->temp =
+                           sis5595_read_value(client, SIS5595_REG_TEMP);
+                       data->temp_over =
+                           sis5595_read_value(client, SIS5595_REG_TEMP_OVER);
+                       data->temp_hyst =
+                           sis5595_read_value(client, SIS5595_REG_TEMP_HYST);
+               }
+               i = sis5595_read_value(client, SIS5595_REG_FANDIV);
+               data->fan_div[0] = (i >> 4) & 0x03;
+               data->fan_div[1] = i >> 6;
+               data->alarms =
+                   sis5595_read_value(client, SIS5595_REG_ALARM1) |
+                   (sis5595_read_value(client, SIS5595_REG_ALARM2) << 8);
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       up(&data->update_lock);
+
+       return data;
+}
+
+static struct pci_device_id sis5595_pci_ids[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) },
+       { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, sis5595_pci_ids);
+
+static int blacklist[] __devinitdata = {
+       PCI_DEVICE_ID_SI_540,
+       PCI_DEVICE_ID_SI_550,
+       PCI_DEVICE_ID_SI_630,
+       PCI_DEVICE_ID_SI_645,
+       PCI_DEVICE_ID_SI_730,
+       PCI_DEVICE_ID_SI_735,
+       PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but
+                                 that ID shows up in other chips so we
+                                 use the 5511 ID for recognition */
+       PCI_DEVICE_ID_SI_5597,
+       PCI_DEVICE_ID_SI_5598,
+       0 };
+
+static int __devinit sis5595_pci_probe(struct pci_dev *dev,
+                                      const struct pci_device_id *id)
+{
+       u16 val;
+       int *i;
+       int addr = 0;
+
+       for (i = blacklist; *i != 0; i++) {
+               struct pci_dev *dev;
+               dev = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL);
+               if (dev) {
+                       dev_err(&dev->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i);
+                       pci_dev_put(dev);
+                       return -ENODEV;
+               }
+       }
+       
+       if (PCIBIOS_SUCCESSFUL !=
+           pci_read_config_word(dev, SIS5595_BASE_REG, &val))
+               return -ENODEV;
+       
+       addr = val & ~(SIS5595_EXTENT - 1);
+       if (addr == 0 && force_addr == 0) {
+               dev_err(&dev->dev, "Base address not set - upgrade BIOS or use force_addr=0xaddr\n");
+               return -ENODEV;
+       }
+       if (force_addr)
+               addr = force_addr;      /* so detect will get called */
+
+       if (!addr) {
+               dev_err(&dev->dev,"No SiS 5595 sensors found.\n");
+               return -ENODEV;
+       }
+       normal_isa[0] = addr;
+
+       s_bridge = pci_dev_get(dev);
+       if (i2c_add_driver(&sis5595_driver)) {
+               pci_dev_put(s_bridge);
+               s_bridge = NULL;
+       }
+
+       /* Always return failure here.  This is to allow other drivers to bind
+        * to this pci device.  We don't really want to have control over the
+        * pci device, we only wanted to read as few register values from it.
+        */
+       return -ENODEV;
+}
+
+static struct pci_driver sis5595_pci_driver = {
+       .name            = "sis5595",
+       .id_table        = sis5595_pci_ids,
+       .probe           = sis5595_pci_probe,
+};
+
+static int __init sm_sis5595_init(void)
+{
+       return pci_register_driver(&sis5595_pci_driver);
+}
+
+static void __exit sm_sis5595_exit(void)
+{
+       pci_unregister_driver(&sis5595_pci_driver);
+       if (s_bridge != NULL) {
+               i2c_del_driver(&sis5595_driver);
+               pci_dev_put(s_bridge);
+               s_bridge = NULL;
+       }
+}
+
+MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>");
+MODULE_DESCRIPTION("SiS 5595 Sensor device");
+MODULE_LICENSE("GPL");
+
+module_init(sm_sis5595_init);
+module_exit(sm_sis5595_exit);
diff --git a/drivers/infiniband/hw/mthca/mthca_uar.c b/drivers/infiniband/hw/mthca/mthca_uar.c
new file mode 100644 (file)
index 0000000..1c8791d
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 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_dev.h"
+#include "mthca_memfree.h"
+
+int mthca_uar_alloc(struct mthca_dev *dev, struct mthca_uar *uar)
+{
+       uar->index = mthca_alloc(&dev->uar_table.alloc);
+       if (uar->index == -1)
+               return -ENOMEM;
+
+       uar->pfn = (pci_resource_start(dev->pdev, 2) >> PAGE_SHIFT) + uar->index;
+
+       return 0;
+}
+
+void mthca_uar_free(struct mthca_dev *dev, struct mthca_uar *uar)
+{
+       mthca_free(&dev->uar_table.alloc, uar->index);
+}
+
+int mthca_init_uar_table(struct mthca_dev *dev)
+{
+       int ret;
+
+       ret = mthca_alloc_init(&dev->uar_table.alloc,
+                              dev->limits.num_uars,
+                              dev->limits.num_uars - 1,
+                              dev->limits.reserved_uars);
+       if (ret)
+               return ret;
+
+       ret = mthca_init_db_tab(dev);
+       if (ret)
+               mthca_alloc_cleanup(&dev->uar_table.alloc);
+
+       return ret;
+}
+
+void mthca_cleanup_uar_table(struct mthca_dev *dev)
+{
+       mthca_cleanup_db_tab(dev);
+
+       /* XXX check if any UARs are still allocated? */
+       mthca_alloc_cleanup(&dev->uar_table.alloc);
+}
diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c
new file mode 100644 (file)
index 0000000..0f1220a
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ *  Keyboard driver for Sharp Corgi models (SL-C7xx)
+ *
+ *  Copyright (c) 2004-2005 Richard Purdie
+ *
+ *  Based on xtkbd.c/locomkbd.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/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/irq.h>
+
+#include <asm/arch/corgi.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/hardware/scoop.h>
+
+#define KB_ROWS                                8
+#define KB_COLS                                12
+#define KB_ROWMASK(r)          (1 << (r))
+#define SCANCODE(r,c)          ( ((r)<<4) + (c) + 1 )
+/* zero code, 124 scancodes + 3 hinge combinations */
+#define        NR_SCANCODES            ( SCANCODE(KB_ROWS-1,KB_COLS-1) +1 +1 +3 )
+#define SCAN_INTERVAL          (HZ/10)
+#define CORGIKBD_PRESSED       1
+
+#define HINGE_SCAN_INTERVAL            (HZ/4)
+
+#define CORGI_KEY_CALENDER     KEY_F1
+#define CORGI_KEY_ADDRESS      KEY_F2
+#define CORGI_KEY_FN           KEY_F3
+#define CORGI_KEY_OFF          KEY_SUSPEND
+#define CORGI_KEY_EXOK         KEY_F5
+#define CORGI_KEY_EXCANCEL     KEY_F6
+#define CORGI_KEY_EXJOGDOWN    KEY_F7
+#define CORGI_KEY_EXJOGUP      KEY_F8
+#define CORGI_KEY_JAP1         KEY_LEFTCTRL
+#define CORGI_KEY_JAP2         KEY_LEFTALT
+#define CORGI_KEY_OK           KEY_F11
+#define CORGI_KEY_MENU         KEY_F12
+#define CORGI_HINGE_0          KEY_KP0
+#define CORGI_HINGE_1          KEY_KP1
+#define CORGI_HINGE_2          KEY_KP2
+
+static unsigned char corgikbd_keycode[NR_SCANCODES] = {
+       0,                                                                                                                /* 0 */
+       0, KEY_1, KEY_3, KEY_5, KEY_6, KEY_7, KEY_9, KEY_0, KEY_BACKSPACE, 0, 0, 0, 0, 0, 0, 0,                               /* 1-16 */
+       0, KEY_2, KEY_4, KEY_R, KEY_Y, KEY_8, KEY_I, KEY_O, KEY_P, 0, 0, 0, 0, 0, 0, 0,                                   /* 17-32 */
+       KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0,                                 /* 33-48 */
+       CORGI_KEY_CALENDER, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0,         /* 49-64 */
+       CORGI_KEY_ADDRESS, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, 0, KEY_LEFTSHIFT, 0, 0, 0, 0, 0,     /* 65-80 */
+       KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0,            /* 81-96 */
+       KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0,  /* 97-112 */
+       CORGI_KEY_OFF, CORGI_KEY_EXOK, CORGI_KEY_EXCANCEL, CORGI_KEY_EXJOGDOWN, CORGI_KEY_EXJOGUP, 0, 0, 0, 0, 0, 0, 0,   /* 113-124 */
+       CORGI_HINGE_0, CORGI_HINGE_1, CORGI_HINGE_2       /* 125-127 */
+};
+
+
+struct corgikbd {
+       unsigned char keycode[ARRAY_SIZE(corgikbd_keycode)];
+       struct input_dev input;
+       char phys[32];
+
+       unsigned char state[ARRAY_SIZE(corgikbd_keycode)];
+       spinlock_t lock;
+
+       struct timer_list timer;
+       struct timer_list htimer;
+};
+
+static void handle_scancode(unsigned int pressed,unsigned int scancode, struct corgikbd *corgikbd_data)
+{
+       if (pressed && !(corgikbd_data->state[scancode] & CORGIKBD_PRESSED)) {
+               corgikbd_data->state[scancode] |= CORGIKBD_PRESSED;
+               input_report_key(&corgikbd_data->input, corgikbd_data->keycode[scancode], 1);
+               if (corgikbd_data->keycode[scancode] == CORGI_KEY_OFF)
+                       input_event(&corgikbd_data->input, EV_PWR, CORGI_KEY_OFF, 1);
+       } else if (!pressed && corgikbd_data->state[scancode] & CORGIKBD_PRESSED) {
+               corgikbd_data->state[scancode] &= ~CORGIKBD_PRESSED;
+               input_report_key(&corgikbd_data->input, corgikbd_data->keycode[scancode], 0);
+       }
+}
+
+#define KB_DISCHARGE_DELAY     10
+#define KB_ACTIVATE_DELAY      10
+
+/* Helper functions for reading the keyboard matrix
+ * Note: We should really be using pxa_gpio_mode to alter GPDR but it
+ *       requires a function call per GPIO bit which is excessive
+ *       when we need to access 12 bits at once multiple times.
+ * These functions must be called within local_irq_save()/local_irq_restore()
+ * or similar.
+ */
+static inline void corgikbd_discharge_all(void)
+{
+       // STROBE All HiZ
+       GPCR2  = CORGI_GPIO_ALL_STROBE_BIT;
+       GPDR2 &= ~CORGI_GPIO_ALL_STROBE_BIT;
+}
+
+static inline void corgikbd_activate_all(void)
+{
+       // STROBE ALL -> High
+       GPSR2  = CORGI_GPIO_ALL_STROBE_BIT;
+       GPDR2 |= CORGI_GPIO_ALL_STROBE_BIT;
+
+       udelay(KB_DISCHARGE_DELAY);
+
+       // Clear any interrupts we may have triggered when altering the GPIO lines
+       GEDR1 = CORGI_GPIO_HIGH_SENSE_BIT;
+       GEDR2 = CORGI_GPIO_LOW_SENSE_BIT;
+}
+
+static inline void corgikbd_activate_col(int col)
+{
+       // STROBE col -> High, not col -> HiZ
+       GPSR2 = CORGI_GPIO_STROBE_BIT(col);
+       GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col);
+}
+
+static inline void corgikbd_reset_col(int col)
+{
+       // STROBE col -> Low
+       GPCR2 = CORGI_GPIO_STROBE_BIT(col);
+       // STROBE col -> out, not col -> HiZ
+       GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col);
+}
+
+#define GET_ROWS_STATUS(c)     (((GPLR1 & CORGI_GPIO_HIGH_SENSE_BIT) >> CORGI_GPIO_HIGH_SENSE_RSHIFT) | ((GPLR2 & CORGI_GPIO_LOW_SENSE_BIT) << CORGI_GPIO_LOW_SENSE_LSHIFT))
+
+/*
+ * The corgi keyboard only generates interrupts when a key is pressed.
+ * When a key is pressed, we enable a timer which then scans the
+ * keyboard to detect when the key is released.
+ */
+
+/* Scan the hardware keyboard and push any changes up through the input layer */
+static void corgikbd_scankeyboard(struct corgikbd *corgikbd_data, struct pt_regs *regs)
+{
+       unsigned int row, col, rowd, scancode;
+       unsigned long flags;
+       unsigned int num_pressed;
+
+       spin_lock_irqsave(&corgikbd_data->lock, flags);
+
+       if (regs)
+               input_regs(&corgikbd_data->input, regs);
+
+       num_pressed = 0;
+       for (col = 0; col < KB_COLS; col++) {
+               /*
+                * Discharge the output driver capacitatance
+                * in the keyboard matrix. (Yes it is significant..)
+                */
+
+               corgikbd_discharge_all();
+               udelay(KB_DISCHARGE_DELAY);
+
+               corgikbd_activate_col(col);
+               udelay(KB_ACTIVATE_DELAY);
+
+               rowd = GET_ROWS_STATUS(col);
+               for (row = 0; row < KB_ROWS; row++) {
+                       scancode = SCANCODE(row, col);
+                       handle_scancode((rowd & KB_ROWMASK(row)), scancode, corgikbd_data);
+                       if (rowd & KB_ROWMASK(row))
+                               num_pressed++;
+               }
+               corgikbd_reset_col(col);
+       }
+
+       corgikbd_activate_all();
+
+       input_sync(&corgikbd_data->input);
+
+       /* if any keys are pressed, enable the timer */
+       if (num_pressed)
+               mod_timer(&corgikbd_data->timer, jiffies + SCAN_INTERVAL);
+
+       spin_unlock_irqrestore(&corgikbd_data->lock, flags);
+}
+
+/*
+ * corgi keyboard interrupt handler.
+ */
+static irqreturn_t corgikbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct corgikbd *corgikbd_data = dev_id;
+
+       if (!timer_pending(&corgikbd_data->timer)) {
+               /** wait chattering delay **/
+               udelay(20);
+               corgikbd_scankeyboard(corgikbd_data, regs);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * corgi timer checking for released keys
+ */
+static void corgikbd_timer_callback(unsigned long data)
+{
+       struct corgikbd *corgikbd_data = (struct corgikbd *) data;
+       corgikbd_scankeyboard(corgikbd_data, NULL);
+}
+
+/*
+ * The hinge switches generate no interrupt so they need to be
+ * monitored by a timer.
+ *
+ * When we detect changes, we debounce it and then pass the three
+ * positions the system can take as keypresses to the input system.
+ */
+
+#define HINGE_STABLE_COUNT 2
+static int sharpsl_hinge_state;
+static int hinge_count;
+
+static void corgikbd_hinge_timer(unsigned long data)
+{
+       struct corgikbd *corgikbd_data = (struct corgikbd *) data;
+       unsigned long gprr;
+       unsigned long flags;
+
+       gprr = read_scoop_reg(SCOOP_GPRR) & (CORGI_SCP_SWA | CORGI_SCP_SWB);
+       if (gprr != sharpsl_hinge_state) {
+               hinge_count = 0;
+               sharpsl_hinge_state = gprr;
+       } else if (hinge_count < HINGE_STABLE_COUNT) {
+               hinge_count++;
+               if (hinge_count >= HINGE_STABLE_COUNT) {
+                       spin_lock_irqsave(&corgikbd_data->lock, flags);
+
+                       handle_scancode((sharpsl_hinge_state == 0x00), 125, corgikbd_data); /* Keyboard with Landscape Screen */
+                       handle_scancode((sharpsl_hinge_state == 0x08), 126, corgikbd_data); /* No Keyboard with Portrait Screen */
+                       handle_scancode((sharpsl_hinge_state == 0x0c), 127, corgikbd_data); /* Keyboard and Screen Closed  */
+                       input_sync(&corgikbd_data->input);
+
+                       spin_unlock_irqrestore(&corgikbd_data->lock, flags);
+               }
+       }
+       mod_timer(&corgikbd_data->htimer, jiffies + HINGE_SCAN_INTERVAL);
+}
+
+static int __init corgikbd_probe(struct device *dev)
+{
+       int i;
+       struct corgikbd *corgikbd;
+
+       corgikbd = kcalloc(1, sizeof(struct corgikbd), GFP_KERNEL);
+       if (!corgikbd)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev,corgikbd);
+       strcpy(corgikbd->phys, "corgikbd/input0");
+
+       spin_lock_init(corgikbd->lock);
+
+       /* Init Keyboard rescan timer */
+       init_timer(&corgikbd->timer);
+       corgikbd->timer.function = corgikbd_timer_callback;
+       corgikbd->timer.data = (unsigned long) corgikbd;
+
+       /* Init Hinge Timer */
+       init_timer(&corgikbd->htimer);
+       corgikbd->htimer.function = corgikbd_hinge_timer;
+       corgikbd->htimer.data = (unsigned long) corgikbd;
+
+       init_input_dev(&corgikbd->input);
+       corgikbd->input.private = corgikbd;
+       corgikbd->input.name = "Corgi Keyboard";
+       corgikbd->input.dev = dev;
+       corgikbd->input.phys = corgikbd->phys;
+       corgikbd->input.id.bustype = BUS_HOST;
+       corgikbd->input.id.vendor = 0x0001;
+       corgikbd->input.id.product = 0x0001;
+       corgikbd->input.id.version = 0x0100;
+       corgikbd->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR);
+       corgikbd->input.keycode = corgikbd->keycode;
+       corgikbd->input.keycodesize = sizeof(unsigned char);
+       corgikbd->input.keycodemax = ARRAY_SIZE(corgikbd_keycode);
+
+       memcpy(corgikbd->keycode, corgikbd_keycode, sizeof(corgikbd->keycode));
+       for (i = 0; i < ARRAY_SIZE(corgikbd_keycode); i++)
+               set_bit(corgikbd->keycode[i], corgikbd->input.keybit);
+       clear_bit(0, corgikbd->input.keybit);
+
+       input_register_device(&corgikbd->input);
+       mod_timer(&corgikbd->htimer, jiffies + HINGE_SCAN_INTERVAL);
+
+       /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
+       for (i = 0; i < CORGI_KEY_SENSE_NUM; i++) {
+               pxa_gpio_mode(CORGI_GPIO_KEY_SENSE(i) | GPIO_IN);
+               if (request_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd_interrupt,
+                                               SA_INTERRUPT, "corgikbd", corgikbd))
+                       printk(KERN_WARNING "corgikbd: Can't get IRQ: %d!\n", i);
+               else
+                       set_irq_type(CORGI_IRQ_GPIO_KEY_SENSE(i),IRQT_RISING);
+       }
+
+       /* Set Strobe lines as outputs - set high */
+       for (i = 0; i < CORGI_KEY_STROBE_NUM; i++)
+               pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
+
+       printk(KERN_INFO "input: Corgi Keyboard Registered\n");
+
+       return 0;
+}
+
+static int corgikbd_remove(struct device *dev)
+{
+       int i;
+       struct corgikbd *corgikbd = dev_get_drvdata(dev);
+
+       for (i = 0; i < CORGI_KEY_SENSE_NUM; i++)
+               free_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd);
+
+       del_timer_sync(&corgikbd->htimer);
+       del_timer_sync(&corgikbd->timer);
+
+       input_unregister_device(&corgikbd->input);
+
+       kfree(corgikbd);
+
+       return 0;
+}
+
+static struct device_driver corgikbd_driver = {
+       .name           = "corgi-keyboard",
+       .bus            = &platform_bus_type,
+       .probe          = corgikbd_probe,
+       .remove         = corgikbd_remove,
+};
+
+static int __devinit corgikbd_init(void)
+{
+       return driver_register(&corgikbd_driver);
+}
+
+static void __exit corgikbd_exit(void)
+{
+       driver_unregister(&corgikbd_driver);
+}
+
+module_init(corgikbd_init);
+module_exit(corgikbd_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
+MODULE_DESCRIPTION("Corgi Keyboard Driver");
+MODULE_LICENSE("GPLv2");
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
new file mode 100644 (file)
index 0000000..ef78bff
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * Generic linux-input device driver for keyboard devices
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("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
+ *
+ * References:
+ * HP-HIL Technical Reference Manual.  Hewlett Packard Product No. 45918A
+ *
+ */
+
+#include <linux/hil.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci_ids.h>
+
+#define PREFIX "HIL KEYB: "
+#define HIL_GENERIC_NAME "HIL keyboard"
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define HIL_KBD_MAX_LENGTH 16
+
+#define HIL_KBD_SET1_UPBIT 0x01
+#define HIL_KBD_SET1_SHIFT 1
+static unsigned int hil_kbd_set1[HIL_KEYCODES_SET1_TBLSIZE] = 
+       { HIL_KEYCODES_SET1 };
+
+#define HIL_KBD_SET2_UPBIT 0x01
+#define HIL_KBD_SET2_SHIFT 1
+/* Set2 is user defined */
+
+#define HIL_KBD_SET3_UPBIT 0x80
+#define HIL_KBD_SET3_SHIFT 0
+static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] =
+       { HIL_KEYCODES_SET3 };
+
+static char hil_language[][16] = { HIL_LOCALE_MAP };
+
+struct hil_kbd {
+       struct input_dev dev;
+       struct serio *serio;
+
+       /* Input buffer and index for packets from HIL bus. */
+       hil_packet data[HIL_KBD_MAX_LENGTH];
+       int idx4; /* four counts per packet */
+
+       /* Raw device info records from HIL bus, see hil.h for fields. */
+       char    idd[HIL_KBD_MAX_LENGTH];        /* DID byte and IDD record */
+       char    rsc[HIL_KBD_MAX_LENGTH];        /* RSC record */
+       char    exd[HIL_KBD_MAX_LENGTH];        /* EXD record */
+       char    rnm[HIL_KBD_MAX_LENGTH + 1];    /* RNM record + NULL term. */
+
+       /* Something to sleep around with. */
+       struct semaphore sem;
+};
+
+/* Process a complete packet after transfer from the HIL */
+static void hil_kbd_process_record(struct hil_kbd *kbd)
+{
+       struct input_dev *dev = &kbd->dev;
+       hil_packet *data = kbd->data;
+       hil_packet p;
+       int idx, i, cnt;
+
+       idx = kbd->idx4/4;
+       p = data[idx - 1];
+
+       if ((p & ~HIL_CMDCT_POL) == 
+           (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) goto report;
+       if ((p & ~HIL_CMDCT_RPL) == 
+           (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) goto report;
+
+       /* Not a poll response.  See if we are loading config records. */
+       switch (p & HIL_PKT_DATA_MASK) {
+       case HIL_CMD_IDD:
+               for (i = 0; i < idx; i++)
+                       kbd->idd[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+               for (; i < HIL_KBD_MAX_LENGTH; i++)
+                       kbd->idd[i] = 0;
+               break;
+       case HIL_CMD_RSC:
+               for (i = 0; i < idx; i++)
+                       kbd->rsc[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+               for (; i < HIL_KBD_MAX_LENGTH; i++)
+                       kbd->rsc[i] = 0;
+               break;
+       case HIL_CMD_EXD:
+               for (i = 0; i < idx; i++)
+                       kbd->exd[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+               for (; i < HIL_KBD_MAX_LENGTH; i++)
+                       kbd->exd[i] = 0;
+               break;
+       case HIL_CMD_RNM:
+               for (i = 0; i < idx; i++)
+                       kbd->rnm[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+               for (; i < HIL_KBD_MAX_LENGTH + 1; i++)
+                       kbd->rnm[i] = '\0';
+               break;
+       default:
+               /* These occur when device isn't present */
+               if (p == (HIL_ERR_INT | HIL_PKT_CMD)) break; 
+               /* Anything else we'd like to know about. */
+               printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
+               break;
+       }
+       goto out;
+
+ report:
+       cnt = 1;
+       switch (kbd->data[0] & HIL_POL_CHARTYPE_MASK) {
+       case HIL_POL_CHARTYPE_NONE:
+               break;
+       case HIL_POL_CHARTYPE_ASCII:
+               while (cnt < idx - 1)
+                       input_report_key(dev, kbd->data[cnt++] & 0x7f, 1);
+               break;
+       case HIL_POL_CHARTYPE_RSVD1:
+       case HIL_POL_CHARTYPE_RSVD2:
+       case HIL_POL_CHARTYPE_BINARY:
+               while (cnt < idx - 1)
+                       input_report_key(dev, kbd->data[cnt++], 1);
+               break;
+       case HIL_POL_CHARTYPE_SET1:
+               while (cnt < idx - 1) {
+                       unsigned int key;
+                       int up;
+                       key = kbd->data[cnt++];
+                       up = key & HIL_KBD_SET1_UPBIT;
+                       key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+                       key = hil_kbd_set1[key >> HIL_KBD_SET1_SHIFT];
+                       if (key != KEY_RESERVED)
+                               input_report_key(dev, key, !up);
+               }
+               break;
+       case HIL_POL_CHARTYPE_SET2:
+               while (cnt < idx - 1) {
+                       unsigned int key;
+                       int up;
+                       key = kbd->data[cnt++];
+                       up = key & HIL_KBD_SET2_UPBIT;
+                       key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+                       key = key >> HIL_KBD_SET2_SHIFT;
+                       if (key != KEY_RESERVED)
+                               input_report_key(dev, key, !up);
+               }
+               break;
+       case HIL_POL_CHARTYPE_SET3:
+               while (cnt < idx - 1) {
+                       unsigned int key;
+                       int up;
+                       key = kbd->data[cnt++];
+                       up = key & HIL_KBD_SET3_UPBIT;
+                       key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+                       key = hil_kbd_set3[key >> HIL_KBD_SET3_SHIFT];
+                       if (key != KEY_RESERVED)
+                               input_report_key(dev, key, !up);
+               }
+               break;
+       }
+ out:
+       kbd->idx4 = 0;
+       up(&kbd->sem);
+}
+
+static void hil_kbd_process_err(struct hil_kbd *kbd) {
+       printk(KERN_WARNING PREFIX "errored HIL packet\n");
+       kbd->idx4 = 0;
+       up(&kbd->sem);
+}
+
+static irqreturn_t hil_kbd_interrupt(struct serio *serio, 
+             unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+       struct hil_kbd *kbd;
+       hil_packet packet;
+       int idx;
+
+       kbd = (struct hil_kbd *)serio->private;
+       if (kbd == NULL) {
+               BUG();
+               return IRQ_HANDLED;
+       }
+
+       if (kbd->idx4 >= (HIL_KBD_MAX_LENGTH * sizeof(hil_packet))) {
+               hil_kbd_process_err(kbd);
+               return IRQ_HANDLED;
+       }
+       idx = kbd->idx4/4;
+       if (!(kbd->idx4 % 4)) kbd->data[idx] = 0;
+       packet = kbd->data[idx];
+       packet |= ((hil_packet)data) << ((3 - (kbd->idx4 % 4)) * 8);
+       kbd->data[idx] = packet;
+
+       /* Records of N 4-byte hil_packets must terminate with a command. */
+       if ((++(kbd->idx4)) % 4) return IRQ_HANDLED;
+       if ((packet & 0xffff0000) != HIL_ERR_INT) {
+               hil_kbd_process_err(kbd);
+               return IRQ_HANDLED;
+       }
+       if (packet & HIL_PKT_CMD) hil_kbd_process_record(kbd);
+       return IRQ_HANDLED;
+}
+
+static void hil_kbd_disconnect(struct serio *serio)
+{
+       struct hil_kbd *kbd;
+
+       kbd = (struct hil_kbd *)serio->private;
+       if (kbd == NULL) {
+               BUG();
+               return;
+       }
+
+       input_unregister_device(&kbd->dev);
+       serio_close(serio);
+       kfree(kbd);
+}
+
+static void hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+       struct hil_kbd  *kbd;
+       uint8_t         did, *idd;
+       int             i;
+       
+       if (serio->type != (SERIO_HIL_MLC | SERIO_HIL)) return;
+
+       if (!(kbd = kmalloc(sizeof(struct hil_kbd), GFP_KERNEL))) return;
+       memset(kbd, 0, sizeof(struct hil_kbd));
+
+       if (serio_open(serio, drv)) goto bail0;
+
+       serio->private = kbd;
+       kbd->serio = serio;
+       kbd->dev.private = kbd;
+
+       init_MUTEX_LOCKED(&(kbd->sem));
+
+       /* Get device info.  MLC driver supplies devid/status/etc. */
+       serio->write(serio, 0);
+       serio->write(serio, 0);
+       serio->write(serio, HIL_PKT_CMD >> 8);
+       serio->write(serio, HIL_CMD_IDD);
+       down(&(kbd->sem));
+
+       serio->write(serio, 0);
+       serio->write(serio, 0);
+       serio->write(serio, HIL_PKT_CMD >> 8);
+       serio->write(serio, HIL_CMD_RSC);
+       down(&(kbd->sem));
+
+       serio->write(serio, 0);
+       serio->write(serio, 0);
+       serio->write(serio, HIL_PKT_CMD >> 8);
+       serio->write(serio, HIL_CMD_RNM);
+       down(&(kbd->sem));
+
+       serio->write(serio, 0);
+       serio->write(serio, 0);
+       serio->write(serio, HIL_PKT_CMD >> 8);
+       serio->write(serio, HIL_CMD_EXD);
+       down(&(kbd->sem));
+
+       up(&(kbd->sem));
+
+       did = kbd->idd[0];
+       idd = kbd->idd + 1;
+       switch (did & HIL_IDD_DID_TYPE_MASK) {
+       case HIL_IDD_DID_TYPE_KB_INTEGRAL:
+       case HIL_IDD_DID_TYPE_KB_ITF:
+       case HIL_IDD_DID_TYPE_KB_RSVD:
+       case HIL_IDD_DID_TYPE_CHAR:
+               printk(KERN_INFO PREFIX "HIL keyboard found (did = 0x%02x, lang = %s)\n",
+                       did, hil_language[did & HIL_IDD_DID_TYPE_KB_LANG_MASK]);
+               break;
+       default:
+               goto bail1;
+       }
+
+       if(HIL_IDD_NUM_BUTTONS(idd) || HIL_IDD_NUM_AXES_PER_SET(*idd)) {
+               printk(KERN_INFO PREFIX "keyboards only, no combo devices supported.\n");
+               goto bail1;
+       }
+
+
+       kbd->dev.evbit[0]       = BIT(EV_KEY) | BIT(EV_REP);
+       kbd->dev.ledbit[0]      = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
+       kbd->dev.keycodemax     = HIL_KEYCODES_SET1_TBLSIZE;
+       kbd->dev.keycodesize    = sizeof(hil_kbd_set1[0]);
+       kbd->dev.keycode        = hil_kbd_set1;
+       kbd->dev.name           = strlen(kbd->rnm) ? kbd->rnm : HIL_GENERIC_NAME;
+       kbd->dev.phys           = "hpkbd/input0";       /* XXX */
+
+       kbd->dev.id.bustype     = BUS_HIL;
+       kbd->dev.id.vendor      = PCI_VENDOR_ID_HP;
+       kbd->dev.id.product     = 0x0001; /* TODO: get from kbd->rsc */
+       kbd->dev.id.version     = 0x0100; /* TODO: get from kbd->rsc */
+       kbd->dev.dev            = &serio->dev;
+
+       for (i = 0; i < 128; i++) {
+               set_bit(hil_kbd_set1[i], kbd->dev.keybit);
+               set_bit(hil_kbd_set3[i], kbd->dev.keybit);
+       }
+       clear_bit(0, kbd->dev.keybit);
+
+       input_register_device(&kbd->dev);
+       printk(KERN_INFO "input: %s, ID: %d\n",
+               kbd->dev.name, did);
+
+       serio->write(serio, 0);
+       serio->write(serio, 0);
+       serio->write(serio, HIL_PKT_CMD >> 8);
+       serio->write(serio, HIL_CMD_EK1); /* Enable Keyswitch Autorepeat 1 */
+       down(&(kbd->sem));
+       up(&(kbd->sem));
+
+       return;
+ bail1:
+       serio_close(serio);
+ bail0:
+       kfree(kbd);
+}
+
+
+struct serio_driver hil_kbd_serio_drv = {
+       .driver         = {
+               .name   = "hil_kbd",
+       },
+       .description    = "HP HIL keyboard driver",
+       .connect        = hil_kbd_connect,
+       .disconnect     = hil_kbd_disconnect,
+       .interrupt      = hil_kbd_interrupt
+};
+
+static int __init hil_kbd_init(void)
+{
+       serio_register_driver(&hil_kbd_serio_drv);
+        return 0;
+}
+                
+static void __exit hil_kbd_exit(void)
+{
+       serio_unregister_driver(&hil_kbd_serio_drv);
+}
+                        
+module_init(hil_kbd_init);
+module_exit(hil_kbd_exit);
diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c
new file mode 100644 (file)
index 0000000..eecb77d
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ *  linux/drivers/hil/hilkbd.c
+ *
+ *  Copyright (C) 1998 Philip Blundell <philb@gnu.org>
+ *  Copyright (C) 1999 Matthew Wilcox <willy@bofh.ai>
+ *  Copyright (C) 1999-2003 Helge Deller <deller@gmx.de>
+ *
+ *  Very basic HP Human Interface Loop (HIL) driver.
+ *  This driver handles the keyboard on HP300 (m68k) and on some 
+ *  HP700 (parisc) series machines.
+ *
+ * 
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2.  See the file COPYING in the main directory of this
+ * archive for more details.
+ */
+
+#include <linux/pci_ids.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/hil.h>
+#include <linux/spinlock.h>
+
+
+MODULE_AUTHOR("Philip Blundell, Matthew Wilcox, Helge Deller");
+MODULE_DESCRIPTION("HIL keyboard driver (basic functionality)");
+MODULE_LICENSE("GPL v2");
+
+
+#if defined(CONFIG_PARISC)
+
+ #include <asm/io.h>
+ #include <asm/hardware.h>
+ #include <asm/parisc-device.h>
+ static unsigned long hil_base;        /* HPA for the HIL device */
+ static unsigned int hil_irq;
+ #define HILBASE               hil_base /* HPPA (parisc) port address */
+ #define HIL_DATA              0x800
+ #define HIL_CMD               0x801
+ #define HIL_IRQ               hil_irq
+ #define hil_readb(p)          gsc_readb(p)
+ #define hil_writeb(v,p)       gsc_writeb((v),(p))
+
+#elif defined(CONFIG_HP300)
+
+ #define HILBASE               0xf0428000 /* HP300 (m86k) port address */
+ #define HIL_DATA              0x1
+ #define HIL_CMD               0x3
+ #define HIL_IRQ               2
+ #define hil_readb(p)          readb(p)
+ #define hil_writeb(v,p)       writeb((v),(p))
+
+#else
+#error "HIL is not supported on this platform"
+#endif
+
+
+/* HIL helper functions */
+#define hil_busy()              (hil_readb(HILBASE + HIL_CMD) & HIL_BUSY)
+#define hil_data_available()    (hil_readb(HILBASE + HIL_CMD) & HIL_DATA_RDY)
+#define hil_status()            (hil_readb(HILBASE + HIL_CMD))
+#define hil_command(x)          do { hil_writeb((x), HILBASE + HIL_CMD); } while (0)
+#define hil_read_data()         (hil_readb(HILBASE + HIL_DATA))
+#define hil_write_data(x)       do { hil_writeb((x), HILBASE + HIL_DATA); } while (0)
+
+/* HIL constants */
+#define        HIL_BUSY                0x02
+#define        HIL_DATA_RDY            0x01
+
+#define        HIL_SETARD              0xA0            /* set auto-repeat delay */
+#define        HIL_SETARR              0xA2            /* set auto-repeat rate */
+#define        HIL_SETTONE             0xA3            /* set tone generator */
+#define        HIL_CNMT                0xB2            /* clear nmi */
+#define        HIL_INTON               0x5C            /* Turn on interrupts. */
+#define        HIL_INTOFF              0x5D            /* Turn off interrupts. */
+
+#define        HIL_READKBDSADR         0xF9
+#define        HIL_WRITEKBDSADR        0xE9
+
+static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] = 
+       { HIL_KEYCODES_SET1 };
+
+/* HIL structure */
+static struct {
+       struct input_dev dev;
+
+       unsigned int curdev;
+       
+       unsigned char s;
+       unsigned char c;
+       int valid;
+       
+       unsigned char data[16];
+       unsigned int ptr;
+       spinlock_t lock;
+
+       void *dev_id;   /* native bus device */
+} hil_dev;
+
+
+static void poll_finished(void)
+{
+       int down;
+       int key;
+       unsigned char scode;
+       
+       switch (hil_dev.data[0]) {
+       case 0x40:
+               down = (hil_dev.data[1] & 1) == 0;
+               scode = hil_dev.data[1] >> 1;
+               key = hphilkeyb_keycode[scode];
+               input_report_key(&hil_dev.dev, key, down);
+               break;
+       }
+       hil_dev.curdev = 0;
+}
+
+static inline void handle_status(unsigned char s, unsigned char c)
+{
+       if (c & 0x8) {
+               /* End of block */
+               if (c & 0x10)
+                       poll_finished();
+       } else {
+               if (c & 0x10) {
+                       if (hil_dev.curdev)
+                               poll_finished();  /* just in case */
+                       hil_dev.curdev = c & 7;
+                       hil_dev.ptr = 0;
+               }
+       }
+}
+
+static inline void handle_data(unsigned char s, unsigned char c)
+{
+       if (hil_dev.curdev) {
+               hil_dev.data[hil_dev.ptr++] = c;
+               hil_dev.ptr &= 15;
+       }
+}
+
+
+/* 
+ * Handle HIL interrupts.
+ */
+static irqreturn_t hil_interrupt(int irq, void *handle, struct pt_regs *regs)
+{
+       unsigned char s, c;
+       
+       s = hil_status();
+       c = hil_read_data();
+
+       switch (s >> 4) {
+       case 0x5:
+               handle_status(s, c);
+               break;
+       case 0x6:
+               handle_data(s, c);
+               break;
+       case 0x4:
+               hil_dev.s = s;
+               hil_dev.c = c;
+               mb();
+               hil_dev.valid = 1;
+               break;
+       }
+       return IRQ_HANDLED;
+}
+
+/*
+ * Send a command to the HIL
+ */
+
+static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&hil_dev.lock, flags);
+       while (hil_busy())
+               /* wait */;
+       hil_command(cmd);
+       while (len--) {
+               while (hil_busy())
+                       /* wait */;
+               hil_write_data(*(data++));
+       }
+       spin_unlock_irqrestore(&hil_dev.lock, flags);
+}
+
+
+/*
+ * Initialise HIL. 
+ */
+
+static int __init
+hil_keyb_init(void)
+{
+       unsigned char c;
+       unsigned int i, kbid;
+       wait_queue_head_t hil_wait;
+
+       if (hil_dev.dev.id.bustype) {
+               return -ENODEV; /* already initialized */
+       }
+       
+#if defined(CONFIG_HP300)
+       if (!hwreg_present((void *)(HILBASE + HIL_DATA)))
+               return -ENODEV;
+       
+       request_region(HILBASE+HIL_DATA, 2, "hil");
+#endif
+       
+       request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id);
+
+       /* Turn on interrupts */
+       hil_do(HIL_INTON, NULL, 0);
+
+       /* Look for keyboards */
+       hil_dev.valid = 0;      /* clear any pending data */
+       hil_do(HIL_READKBDSADR, NULL, 0);
+
+       init_waitqueue_head(&hil_wait);
+       wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3*HZ);
+       if (!hil_dev.valid) {
+               printk(KERN_WARNING "HIL: timed out, assuming no keyboard present.\n");
+       }
+
+       c = hil_dev.c; 
+       hil_dev.valid = 0;
+       if (c == 0) {
+               kbid = -1;
+               printk(KERN_WARNING "HIL: no keyboard present.\n");
+       } else {
+               kbid = ffz(~c);
+               /* printk(KERN_INFO "HIL: keyboard found at id %d\n", kbid); */
+       }
+
+       /* set it to raw mode */
+       c = 0;
+       hil_do(HIL_WRITEKBDSADR, &c, 1);
+       
+       init_input_dev(&hil_dev.dev);
+
+       for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++)
+               if (hphilkeyb_keycode[i] != KEY_RESERVED)
+                       set_bit(hphilkeyb_keycode[i], hil_dev.dev.keybit);
+
+       hil_dev.dev.evbit[0]    = BIT(EV_KEY) | BIT(EV_REP);
+       hil_dev.dev.ledbit[0]   = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
+       hil_dev.dev.keycodemax  = HIL_KEYCODES_SET1_TBLSIZE;
+        hil_dev.dev.keycodesize = sizeof(hphilkeyb_keycode[0]);
+       hil_dev.dev.keycode     = hphilkeyb_keycode;
+       hil_dev.dev.name        = "HIL keyboard";
+       hil_dev.dev.phys        = "hpkbd/input0";
+
+       hil_dev.dev.id.bustype  = BUS_HIL;
+       hil_dev.dev.id.vendor   = PCI_VENDOR_ID_HP;
+       hil_dev.dev.id.product  = 0x0001;
+       hil_dev.dev.id.version  = 0x0010;
+
+       input_register_device(&hil_dev.dev);
+       printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n",
+               hil_dev.dev.name, kbid, HILBASE, HIL_IRQ);
+
+       return 0;
+}
+
+#if defined(CONFIG_PARISC)
+static int __init
+hil_init_chip(struct parisc_device *dev)
+{
+       if (!dev->irq) {
+               printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%08lx\n", dev->hpa);
+               return -ENODEV;
+       }
+
+       hil_base = dev->hpa;
+       hil_irq  = dev->irq;
+       hil_dev.dev_id = dev;
+       
+       printk(KERN_INFO "Found HIL bus at 0x%08lx, IRQ %d\n", hil_base, hil_irq);
+
+       return hil_keyb_init();
+}
+
+static struct parisc_device_id hil_tbl[] = {
+       { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00073 },
+       { 0, }
+};
+
+MODULE_DEVICE_TABLE(parisc, hil_tbl);
+
+static struct parisc_driver hil_driver = {
+       .name =         "HIL",
+       .id_table =     hil_tbl,
+       .probe =        hil_init_chip,
+};
+#endif /* CONFIG_PARISC */
+
+
+
+
+
+static int __init hil_init(void)
+{
+#if defined(CONFIG_PARISC)
+       return register_parisc_driver(&hil_driver);
+#else
+       return hil_keyb_init();
+#endif
+}
+
+
+static void __exit hil_exit(void)
+{
+       if (HIL_IRQ) {
+               disable_irq(HIL_IRQ);
+               free_irq(HIL_IRQ, hil_dev.dev_id);
+       }
+
+       /* Turn off interrupts */
+       hil_do(HIL_INTOFF, NULL, 0);
+
+       input_unregister_device(&hil_dev.dev);
+
+#if defined(CONFIG_PARISC)
+       unregister_parisc_driver(&hil_driver);
+#else
+       release_region(HILBASE+HIL_DATA, 2);
+#endif
+}
+
+module_init(hil_init);
+module_exit(hil_exit);
+
diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c
new file mode 100644 (file)
index 0000000..d3e9dd6
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ *  Copyright (c) 2005 John Lenz
+ *
+ * Based on from xtkbd.c
+ */
+
+/*
+ * LoCoMo keyboard driver for Linux/ARM
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+
+#include <asm/hardware/locomo.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
+MODULE_DESCRIPTION("LoCoMo keyboard driver");
+MODULE_LICENSE("GPL");
+
+#define LOCOMOKBD_NUMKEYS      128
+
+#define KEY_ACTIVITY           KEY_F16
+#define KEY_CONTACT            KEY_F18
+#define KEY_CENTER             KEY_F15
+
+static unsigned char locomokbd_keycode[LOCOMOKBD_NUMKEYS] = {
+       0, KEY_ESC, KEY_ACTIVITY, 0, 0, 0, 0, 0, 0, 0,                          /* 0 - 9 */
+       0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_HOME, KEY_CONTACT,                   /* 10 - 19 */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                                           /* 20 - 29 */
+       0, 0, 0, KEY_CENTER, 0, KEY_MAIL, 0, 0, 0, 0,                           /* 30 - 39 */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RIGHT,                                   /* 40 - 49 */
+       KEY_UP, KEY_LEFT, 0, 0, KEY_P, 0, KEY_O, KEY_I, KEY_Y, KEY_T,           /* 50 - 59 */
+       KEY_E, KEY_W, 0, 0, 0, 0, KEY_DOWN, KEY_ENTER, 0, 0,                    /* 60 - 69 */
+       KEY_BACKSPACE, 0, KEY_L, KEY_U, KEY_H, KEY_R, KEY_D, KEY_Q, 0, 0,       /* 70 - 79 */
+       0, 0, 0, 0, 0, 0, KEY_ENTER, KEY_RIGHTSHIFT, KEY_K, KEY_J,              /* 80 - 89 */
+       KEY_G, KEY_F, KEY_X, KEY_S, 0, 0, 0, 0, 0, 0,                           /* 90 - 99 */
+       0, 0, KEY_DOT, 0, KEY_COMMA, KEY_N, KEY_B, KEY_C, KEY_Z, KEY_A,         /* 100 - 109 */
+       KEY_LEFTSHIFT, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0, 0, 0,              /* 110 - 119 */
+       KEY_M, KEY_SPACE, KEY_V, KEY_APOSTROPHE, KEY_SLASH, 0, 0, 0             /* 120 - 128 */
+};
+
+#define KB_ROWS                        16
+#define KB_COLS                        8
+#define KB_ROWMASK(r)          (1 << (r))
+#define SCANCODE(c,r)          ( ((c)<<4) + (r) + 1 )
+#define        NR_SCANCODES            128
+
+#define KB_DELAY               8
+#define SCAN_INTERVAL          (HZ/10)
+#define LOCOMOKBD_PRESSED      1
+
+struct locomokbd {
+       unsigned char keycode[LOCOMOKBD_NUMKEYS];
+       struct input_dev input;
+       char phys[32];
+
+       struct locomo_dev *ldev;
+       unsigned long base;
+       spinlock_t lock;
+       
+       struct timer_list timer;
+};
+
+/* helper functions for reading the keyboard matrix */
+static inline void locomokbd_charge_all(unsigned long membase)
+{
+       locomo_writel(0x00FF, membase + LOCOMO_KSC);
+}
+
+static inline void locomokbd_activate_all(unsigned long membase)
+{
+       unsigned long r;
+       
+       locomo_writel(0, membase + LOCOMO_KSC);
+       r = locomo_readl(membase + LOCOMO_KIC);
+       r &= 0xFEFF;
+       locomo_writel(r, membase + LOCOMO_KIC);
+}
+
+static inline void locomokbd_activate_col(unsigned long membase, int col)
+{
+       unsigned short nset;
+       unsigned short nbset;
+
+       nset = 0xFF & ~(1 << col);
+       nbset = (nset << 8) + nset;
+       locomo_writel(nbset, membase + LOCOMO_KSC);
+}
+
+static inline void locomokbd_reset_col(unsigned long membase, int col)
+{
+       unsigned short nbset;
+
+       nbset = ((0xFF & ~(1 << col)) << 8) + 0xFF;
+       locomo_writel(nbset, membase + LOCOMO_KSC);
+}
+
+/*
+ * The LoCoMo keyboard only generates interrupts when a key is pressed.
+ * So when a key is pressed, we enable a timer.  This timer scans the
+ * keyboard, and this is how we detect when the key is released.
+ */
+
+/* Scan the hardware keyboard and push any changes up through the input layer */
+static void locomokbd_scankeyboard(struct locomokbd *locomokbd, struct pt_regs *regs) 
+{
+       unsigned int row, col, rowd, scancode;
+       unsigned long flags;
+       unsigned int num_pressed;
+       unsigned long membase = locomokbd->base;
+
+       spin_lock_irqsave(&locomokbd->lock, flags);
+
+       if (regs)
+               input_regs(&locomokbd->input, regs);
+       
+       locomokbd_charge_all(membase);
+
+       num_pressed = 0;
+       for (col = 0; col < KB_COLS; col++) {
+
+               locomokbd_activate_col(membase, col);
+               udelay(KB_DELAY);
+                
+               rowd = ~locomo_readl(membase + LOCOMO_KIB);
+               for (row = 0; row < KB_ROWS; row++ ) {
+                       scancode = SCANCODE(col, row);
+                       if (rowd & KB_ROWMASK(row)) {
+                               num_pressed += 1;
+                               input_report_key(&locomokbd->input, locomokbd->keycode[scancode], 1);
+                       } else {
+                               input_report_key(&locomokbd->input, locomokbd->keycode[scancode], 0);
+                       }
+               }
+               locomokbd_reset_col(membase, col);
+       }
+       locomokbd_activate_all(membase);
+
+       input_sync(&locomokbd->input);
+
+       /* if any keys are pressed, enable the timer */
+       if (num_pressed)
+               mod_timer(&locomokbd->timer, jiffies + SCAN_INTERVAL);
+
+       spin_unlock_irqrestore(&locomokbd->lock, flags);
+}
+
+/* 
+ * LoCoMo keyboard interrupt handler.
+ */
+static irqreturn_t locomokbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct locomokbd *locomokbd = dev_id;
+       /** wait chattering delay **/
+       udelay(100);
+
+       locomokbd_scankeyboard(locomokbd, regs);
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * LoCoMo timer checking for released keys
+ */
+static void locomokbd_timer_callback(unsigned long data)
+{
+       struct locomokbd *locomokbd = (struct locomokbd *) data;
+       locomokbd_scankeyboard(locomokbd, NULL);
+}
+
+static int locomokbd_probe(struct locomo_dev *dev)
+{
+       struct locomokbd *locomokbd;
+       int i, ret;
+
+       locomokbd = kmalloc(sizeof(struct locomokbd), GFP_KERNEL);
+       if (!locomokbd)
+               return -ENOMEM;
+
+       memset(locomokbd, 0, sizeof(struct locomokbd));
+
+       /* try and claim memory region */
+       if (!request_mem_region((unsigned long) dev->mapbase, 
+                               dev->length, 
+                               LOCOMO_DRIVER_NAME(dev))) {
+               ret = -EBUSY;
+               printk(KERN_ERR "locomokbd: Can't acquire access to io memory for keyboard\n");
+               goto free;
+       }
+
+       locomokbd->ldev = dev;
+       locomo_set_drvdata(dev, locomokbd);
+
+       locomokbd->base = (unsigned long) dev->mapbase;
+
+       spin_lock_init(&locomokbd->lock);
+
+       init_timer(&locomokbd->timer);
+       locomokbd->timer.function = locomokbd_timer_callback;
+       locomokbd->timer.data = (unsigned long) locomokbd;
+
+       locomokbd->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+       
+       init_input_dev(&locomokbd->input);
+       locomokbd->input.keycode = locomokbd->keycode;
+       locomokbd->input.keycodesize = sizeof(unsigned char);
+       locomokbd->input.keycodemax = ARRAY_SIZE(locomokbd_keycode);
+       locomokbd->input.private = locomokbd;
+
+       memcpy(locomokbd->keycode, locomokbd_keycode, sizeof(locomokbd->keycode));
+       for (i = 0; i < LOCOMOKBD_NUMKEYS; i++)
+               set_bit(locomokbd->keycode[i], locomokbd->input.keybit);
+       clear_bit(0, locomokbd->input.keybit);
+
+       strcpy(locomokbd->phys, "locomokbd/input0");
+
+       locomokbd->input.name = "LoCoMo keyboard";
+       locomokbd->input.phys = locomokbd->phys;
+       locomokbd->input.id.bustype = BUS_XTKBD;
+       locomokbd->input.id.vendor = 0x0001;
+       locomokbd->input.id.product = 0x0001;
+       locomokbd->input.id.version = 0x0100;
+
+       /* attempt to get the interrupt */
+       ret = request_irq(dev->irq[0], locomokbd_interrupt, 0, "locomokbd", locomokbd);
+       if (ret) {
+               printk(KERN_ERR "locomokbd: Can't get irq for keyboard\n");
+               goto out;
+       }
+
+       input_register_device(&locomokbd->input);
+
+       printk(KERN_INFO "input: LoCoMo keyboard on locomokbd\n");
+
+       return 0;
+
+out:
+       release_mem_region((unsigned long) dev->mapbase, dev->length);
+       locomo_set_drvdata(dev, NULL);
+free:
+       kfree(locomokbd);
+
+       return ret;
+}
+
+static int locomokbd_remove(struct locomo_dev *dev)
+{
+       struct locomokbd *locomokbd = locomo_get_drvdata(dev);
+       
+       free_irq(dev->irq[0], locomokbd);
+
+       del_timer_sync(&locomokbd->timer);
+       
+       input_unregister_device(&locomokbd->input);
+       locomo_set_drvdata(dev, NULL);
+
+       release_mem_region((unsigned long) dev->mapbase, dev->length);
+
+       kfree(locomokbd);
+
+       return 0;
+}
+
+static struct locomo_driver keyboard_driver = {
+       .drv = {
+               .name = "locomokbd"
+       },
+       .devid  = LOCOMO_DEVID_KEYBOARD,
+       .probe  = locomokbd_probe,
+       .remove = locomokbd_remove,
+};
+
+static int __init locomokbd_init(void)
+{
+       return locomo_driver_register(&keyboard_driver);
+}
+
+static void __exit locomokbd_exit(void)
+{
+       locomo_driver_unregister(&keyboard_driver);
+}
+
+module_init(locomokbd_init);
+module_exit(locomokbd_exit);
diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
new file mode 100644 (file)
index 0000000..1cd7657
--- /dev/null
@@ -0,0 +1,724 @@
+/*
+ * HP i8042 SDC + MSM-58321 BBRTC driver.
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("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
+ *
+ * References:
+ * System Device Controller Microprocessor Firmware Theory of Operation
+ *      for Part Number 1820-4784 Revision B.  Dwg No. A-1820-4784-2
+ * efirtc.c by Stephane Eranian/Hewlett Packard
+ *
+ */
+
+#include <linux/hp_sdc.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/miscdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/poll.h>
+#include <linux/rtc.h>
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("HP i8042 SDC + MSM-58321 RTC Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define RTC_VERSION "1.10d"
+
+static unsigned long epoch = 2000;
+
+static struct semaphore i8042tregs;
+
+static hp_sdc_irqhook hp_sdc_rtc_isr;
+
+static struct fasync_struct *hp_sdc_rtc_async_queue;
+
+static DECLARE_WAIT_QUEUE_HEAD(hp_sdc_rtc_wait);
+
+static loff_t hp_sdc_rtc_llseek(struct file *file, loff_t offset, int origin);
+
+static ssize_t hp_sdc_rtc_read(struct file *file, char *buf,
+                              size_t count, loff_t *ppos);
+
+static int hp_sdc_rtc_ioctl(struct inode *inode, struct file *file,
+                           unsigned int cmd, unsigned long arg);
+
+static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait);
+
+static int hp_sdc_rtc_open(struct inode *inode, struct file *file);
+static int hp_sdc_rtc_release(struct inode *inode, struct file *file);
+static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on);
+
+static int hp_sdc_rtc_read_proc(char *page, char **start, off_t off,
+                               int count, int *eof, void *data);
+
+static void hp_sdc_rtc_isr (int irq, void *dev_id, 
+                           uint8_t status, uint8_t data) 
+{
+       return;
+}
+
+static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
+{
+       struct semaphore tsem;
+       hp_sdc_transaction t;
+       uint8_t tseq[91];
+       int i;
+       
+       i = 0;
+       while (i < 91) {
+               tseq[i++] = HP_SDC_ACT_DATAREG |
+                       HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN;
+               tseq[i++] = 0x01;                       /* write i8042[0x70] */
+               tseq[i]   = i / 7;                      /* BBRTC reg address */
+               i++;
+               tseq[i++] = HP_SDC_CMD_DO_RTCR;         /* Trigger command   */
+               tseq[i++] = 2;          /* expect 1 stat/dat pair back.   */
+               i++; i++;               /* buffer for stat/dat pair       */
+       }
+       tseq[84] |= HP_SDC_ACT_SEMAPHORE;
+       t.endidx =              91;
+       t.seq =                 tseq;
+       t.act.semaphore =       &tsem;
+       init_MUTEX_LOCKED(&tsem);
+       
+       if (hp_sdc_enqueue_transaction(&t)) return -1;
+       
+       down_interruptible(&tsem);  /* Put ourselves to sleep for results. */
+       
+       /* Check for nonpresence of BBRTC */
+       if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] |
+              tseq[55] | tseq[62] | tseq[34] | tseq[41] |
+              tseq[20] | tseq[27] | tseq[6]  | tseq[13]) & 0x0f))
+               return -1;
+
+       memset(rtctm, 0, sizeof(struct rtc_time));
+       rtctm->tm_year = (tseq[83] & 0x0f) + (tseq[90] & 0x0f) * 10;
+       rtctm->tm_mon  = (tseq[69] & 0x0f) + (tseq[76] & 0x0f) * 10;
+       rtctm->tm_mday = (tseq[55] & 0x0f) + (tseq[62] & 0x0f) * 10;
+       rtctm->tm_wday = (tseq[48] & 0x0f);
+       rtctm->tm_hour = (tseq[34] & 0x0f) + (tseq[41] & 0x0f) * 10;
+       rtctm->tm_min  = (tseq[20] & 0x0f) + (tseq[27] & 0x0f) * 10;
+       rtctm->tm_sec  = (tseq[6]  & 0x0f) + (tseq[13] & 0x0f) * 10;
+       
+       return 0;
+}
+
+static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm)
+{
+       struct rtc_time tm, tm_last;
+       int i = 0;
+
+       /* MSM-58321 has no read latch, so must read twice and compare. */
+
+       if (hp_sdc_rtc_do_read_bbrtc(&tm_last)) return -1;
+       if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
+
+       while (memcmp(&tm, &tm_last, sizeof(struct rtc_time))) {
+               if (i++ > 4) return -1;
+               memcpy(&tm_last, &tm, sizeof(struct rtc_time));
+               if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
+       }
+
+       memcpy(rtctm, &tm, sizeof(struct rtc_time));
+
+       return 0;
+}
+
+
+static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
+{
+       hp_sdc_transaction t;
+       uint8_t tseq[26] = {
+               HP_SDC_ACT_PRECMD | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
+               0,
+               HP_SDC_CMD_READ_T1, 2, 0, 0,
+               HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 
+               HP_SDC_CMD_READ_T2, 2, 0, 0,
+               HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 
+               HP_SDC_CMD_READ_T3, 2, 0, 0,
+               HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 
+               HP_SDC_CMD_READ_T4, 2, 0, 0,
+               HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 
+               HP_SDC_CMD_READ_T5, 2, 0, 0
+       };
+
+       t.endidx = numreg * 5;
+
+       tseq[1] = loadcmd;
+       tseq[t.endidx - 4] |= HP_SDC_ACT_SEMAPHORE; /* numreg assumed > 1 */
+
+       t.seq =                 tseq;
+       t.act.semaphore =       &i8042tregs;
+
+       down_interruptible(&i8042tregs);  /* Sleep if output regs in use. */
+
+       if (hp_sdc_enqueue_transaction(&t)) return -1;
+       
+       down_interruptible(&i8042tregs);  /* Sleep until results come back. */
+       up(&i8042tregs);
+
+       return (tseq[5] | 
+               ((uint64_t)(tseq[10]) << 8)  | ((uint64_t)(tseq[15]) << 16) |
+               ((uint64_t)(tseq[20]) << 24) | ((uint64_t)(tseq[25]) << 32));
+}
+
+
+/* Read the i8042 real-time clock */
+static inline int hp_sdc_rtc_read_rt(struct timeval *res) {
+       int64_t raw;
+       uint32_t tenms; 
+       unsigned int days;
+
+       raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_RT, 5);
+       if (raw < 0) return -1;
+
+       tenms = (uint32_t)raw & 0xffffff;
+       days  = (unsigned int)(raw >> 24) & 0xffff;
+
+       res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+       res->tv_sec =  (time_t)(tenms / 100) + days * 86400;
+
+       return 0;
+}
+
+
+/* Read the i8042 fast handshake timer */
+static inline int hp_sdc_rtc_read_fhs(struct timeval *res) {
+       uint64_t raw;
+       unsigned int tenms;
+
+       raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_FHS, 2);
+       if (raw < 0) return -1;
+
+       tenms = (unsigned int)raw & 0xffff;
+
+       res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+       res->tv_sec  = (time_t)(tenms / 100);
+
+       return 0;
+}
+
+
+/* Read the i8042 match timer (a.k.a. alarm) */
+static inline int hp_sdc_rtc_read_mt(struct timeval *res) {
+       int64_t raw;    
+       uint32_t tenms; 
+
+       raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_MT, 3);
+       if (raw < 0) return -1;
+
+       tenms = (uint32_t)raw & 0xffffff;
+
+       res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+       res->tv_sec  = (time_t)(tenms / 100);
+
+       return 0;
+}
+
+
+/* Read the i8042 delay timer */
+static inline int hp_sdc_rtc_read_dt(struct timeval *res) {
+       int64_t raw;
+       uint32_t tenms;
+
+       raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_DT, 3);
+       if (raw < 0) return -1;
+
+       tenms = (uint32_t)raw & 0xffffff;
+
+       res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+       res->tv_sec  = (time_t)(tenms / 100);
+
+       return 0;
+}
+
+
+/* Read the i8042 cycle timer (a.k.a. periodic) */
+static inline int hp_sdc_rtc_read_ct(struct timeval *res) {
+       int64_t raw;
+       uint32_t tenms;
+
+       raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_CT, 3);
+       if (raw < 0) return -1;
+
+       tenms = (uint32_t)raw & 0xffffff;
+
+       res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+       res->tv_sec  = (time_t)(tenms / 100);
+
+       return 0;
+}
+
+
+/* Set the i8042 real-time clock */
+static int hp_sdc_rtc_set_rt (struct timeval *setto)
+{
+       uint32_t tenms;
+       unsigned int days;
+       hp_sdc_transaction t;
+       uint8_t tseq[11] = {
+               HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+               HP_SDC_CMD_SET_RTMS, 3, 0, 0, 0,
+               HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+               HP_SDC_CMD_SET_RTD, 2, 0, 0 
+       };
+
+       t.endidx = 10;
+
+       if (0xffff < setto->tv_sec / 86400) return -1;
+       days = setto->tv_sec / 86400;
+       if (0xffff < setto->tv_usec / 1000000 / 86400) return -1;
+       days += ((setto->tv_sec % 86400) + setto->tv_usec / 1000000) / 86400;
+       if (days > 0xffff) return -1;
+
+       if (0xffffff < setto->tv_sec) return -1;
+       tenms  = setto->tv_sec * 100;
+       if (0xffffff < setto->tv_usec / 10000) return -1;
+       tenms += setto->tv_usec / 10000;
+       if (tenms > 0xffffff) return -1;
+
+       tseq[3] = (uint8_t)(tenms & 0xff);
+       tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
+       tseq[5] = (uint8_t)((tenms >> 16) & 0xff);
+
+       tseq[9] = (uint8_t)(days & 0xff);
+       tseq[10] = (uint8_t)((days >> 8) & 0xff);
+
+       t.seq = tseq;
+
+       if (hp_sdc_enqueue_transaction(&t)) return -1;
+       return 0;
+}
+
+/* Set the i8042 fast handshake timer */
+static int hp_sdc_rtc_set_fhs (struct timeval *setto)
+{
+       uint32_t tenms;
+       hp_sdc_transaction t;
+       uint8_t tseq[5] = {
+               HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+               HP_SDC_CMD_SET_FHS, 2, 0, 0
+       };
+
+       t.endidx = 4;
+
+       if (0xffff < setto->tv_sec) return -1;
+       tenms  = setto->tv_sec * 100;
+       if (0xffff < setto->tv_usec / 10000) return -1;
+       tenms += setto->tv_usec / 10000;
+       if (tenms > 0xffff) return -1;
+
+       tseq[3] = (uint8_t)(tenms & 0xff);
+       tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
+
+       t.seq = tseq;
+
+       if (hp_sdc_enqueue_transaction(&t)) return -1;
+       return 0;
+}
+
+
+/* Set the i8042 match timer (a.k.a. alarm) */
+#define hp_sdc_rtc_set_mt (setto) \
+       hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_MT)
+
+/* Set the i8042 delay timer */
+#define hp_sdc_rtc_set_dt (setto) \
+       hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_DT)
+
+/* Set the i8042 cycle timer (a.k.a. periodic) */
+#define hp_sdc_rtc_set_ct (setto) \
+       hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_CT)
+
+/* Set one of the i8042 3-byte wide timers */
+static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd)
+{
+       uint32_t tenms;
+       hp_sdc_transaction t;
+       uint8_t tseq[6] = {
+               HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+               0, 3, 0, 0, 0
+       };
+
+       t.endidx = 6;
+
+       if (0xffffff < setto->tv_sec) return -1;
+       tenms  = setto->tv_sec * 100;
+       if (0xffffff < setto->tv_usec / 10000) return -1;
+       tenms += setto->tv_usec / 10000;
+       if (tenms > 0xffffff) return -1;
+
+       tseq[1] = setcmd;
+       tseq[3] = (uint8_t)(tenms & 0xff);
+       tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
+       tseq[5] = (uint8_t)((tenms >> 16)  & 0xff);
+
+       t.seq =                 tseq;
+
+       if (hp_sdc_enqueue_transaction(&t)) { 
+               return -1;
+       }
+       return 0;
+}
+
+static loff_t hp_sdc_rtc_llseek(struct file *file, loff_t offset, int origin)
+{
+        return -ESPIPE;
+}
+
+static ssize_t hp_sdc_rtc_read(struct file *file, char *buf,
+                              size_t count, loff_t *ppos) {
+       ssize_t retval;
+
+        if (count < sizeof(unsigned long))
+                return -EINVAL;
+
+       retval = put_user(68, (unsigned long *)buf);
+       return retval;
+}
+
+static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait)
+{
+        unsigned long l;
+
+       l = 0;
+        if (l != 0)
+                return POLLIN | POLLRDNORM;
+        return 0;
+}
+
+static int hp_sdc_rtc_open(struct inode *inode, struct file *file)
+{
+        return 0;
+}
+
+static int hp_sdc_rtc_release(struct inode *inode, struct file *file)
+{
+       /* Turn off interrupts? */
+
+        if (file->f_flags & FASYNC) {
+                hp_sdc_rtc_fasync (-1, file, 0);
+        }
+
+        return 0;
+}
+
+static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on)
+{
+        return fasync_helper (fd, filp, on, &hp_sdc_rtc_async_queue);
+}
+
+static int hp_sdc_rtc_proc_output (char *buf)
+{
+#define YN(bit) ("no")
+#define NY(bit) ("yes")
+        char *p;
+        struct rtc_time tm;
+       struct timeval tv;
+
+       memset(&tm, 0, sizeof(struct rtc_time));
+
+       p = buf;
+
+       if (hp_sdc_rtc_read_bbrtc(&tm)) {
+               p += sprintf(p, "BBRTC\t\t: READ FAILED!\n");
+       } else {
+               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, epoch);
+       }
+
+       if (hp_sdc_rtc_read_rt(&tv)) {
+               p += sprintf(p, "i8042 rtc\t: READ FAILED!\n");
+       } else {
+               p += sprintf(p, "i8042 rtc\t: %ld.%02d seconds\n", 
+                            tv.tv_sec, tv.tv_usec/1000);
+       }
+
+       if (hp_sdc_rtc_read_fhs(&tv)) {
+               p += sprintf(p, "handshake\t: READ FAILED!\n");
+       } else {
+               p += sprintf(p, "handshake\t: %ld.%02d seconds\n", 
+                            tv.tv_sec, tv.tv_usec/1000);
+       }
+
+       if (hp_sdc_rtc_read_mt(&tv)) {
+               p += sprintf(p, "alarm\t\t: READ FAILED!\n");
+       } else {
+               p += sprintf(p, "alarm\t\t: %ld.%02d seconds\n", 
+                            tv.tv_sec, tv.tv_usec/1000);
+       }
+
+       if (hp_sdc_rtc_read_dt(&tv)) {
+               p += sprintf(p, "delay\t\t: READ FAILED!\n");
+       } else {
+               p += sprintf(p, "delay\t\t: %ld.%02d seconds\n", 
+                            tv.tv_sec, tv.tv_usec/1000);
+       }
+
+       if (hp_sdc_rtc_read_ct(&tv)) {
+               p += sprintf(p, "periodic\t: READ FAILED!\n");
+       } else {
+               p += sprintf(p, "periodic\t: %ld.%02d seconds\n", 
+                            tv.tv_sec, tv.tv_usec/1000);
+       }
+
+        p += sprintf(p,
+                     "DST_enable\t: %s\n"
+                     "BCD\t\t: %s\n"
+                     "24hr\t\t: %s\n"
+                     "square_wave\t: %s\n"
+                     "alarm_IRQ\t: %s\n"
+                     "update_IRQ\t: %s\n"
+                     "periodic_IRQ\t: %s\n"
+                    "periodic_freq\t: %ld\n"
+                     "batt_status\t: %s\n",
+                     YN(RTC_DST_EN),
+                     NY(RTC_DM_BINARY),
+                     YN(RTC_24H),
+                     YN(RTC_SQWE),
+                     YN(RTC_AIE),
+                     YN(RTC_UIE),
+                     YN(RTC_PIE),
+                     1UL,
+                     1 ? "okay" : "dead");
+
+        return  p - buf;
+#undef YN
+#undef NY
+}
+
+static int hp_sdc_rtc_read_proc(char *page, char **start, off_t off,
+                         int count, int *eof, void *data)
+{
+       int len = hp_sdc_rtc_proc_output (page);
+        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 hp_sdc_rtc_ioctl(struct inode *inode, struct file *file, 
+                           unsigned int cmd, unsigned long arg)
+{
+#if 1
+       return -EINVAL;
+#else
+       
+        struct rtc_time wtime; 
+       struct timeval ttime;
+       int use_wtime = 0;
+
+       /* This needs major work. */
+
+        switch (cmd) {
+
+        case RTC_AIE_OFF:       /* Mask alarm int. enab. bit    */
+        case RTC_AIE_ON:        /* Allow alarm interrupts.      */
+       case RTC_PIE_OFF:       /* Mask periodic int. enab. bit */
+        case RTC_PIE_ON:        /* Allow periodic ints          */
+        case RTC_UIE_ON:        /* Allow ints for RTC updates.  */
+        case RTC_UIE_OFF:       /* Allow ints for RTC updates.  */
+        {
+               /* We cannot mask individual user timers and we
+                  cannot tell them apart when they occur, so it 
+                  would be disingenuous to succeed these IOCTLs */
+               return -EINVAL;
+        }
+        case RTC_ALM_READ:      /* Read the present alarm time */
+        {
+               if (hp_sdc_rtc_read_mt(&ttime)) return -EFAULT;
+               if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
+
+               wtime.tm_hour = ttime.tv_sec / 3600;  ttime.tv_sec %= 3600;
+               wtime.tm_min  = ttime.tv_sec / 60;    ttime.tv_sec %= 60;
+               wtime.tm_sec  = ttime.tv_sec;
+                
+               break;
+        }
+        case RTC_IRQP_READ:     /* Read the periodic IRQ rate.  */
+        {
+                return put_user(hp_sdc_rtc_freq, (unsigned long *)arg);
+        }
+        case RTC_IRQP_SET:      /* Set periodic IRQ rate.       */
+        {
+                /* 
+                 * The max we can do is 100Hz.
+                */
+
+                if ((arg < 1) || (arg > 100)) return -EINVAL;
+               ttime.tv_sec = 0;
+               ttime.tv_usec = 1000000 / arg;
+               if (hp_sdc_rtc_set_ct(&ttime)) return -EFAULT;
+               hp_sdc_rtc_freq = arg;
+                return 0;
+        }
+        case RTC_ALM_SET:       /* Store a time into the alarm */
+        {
+                /*
+                 * This expects a struct hp_sdc_rtc_time. Writing 0xff means
+                 * "don't care" or "match all" for PC timers.  The HP SDC
+                * does not support that perk, but it could be emulated fairly
+                * easily.  Only the tm_hour, tm_min and tm_sec are used.
+                * We could do it with 10ms accuracy with the HP SDC, if the 
+                * rtc interface left us a way to do that.
+                 */
+                struct hp_sdc_rtc_time alm_tm;
+
+                if (copy_from_user(&alm_tm, (struct hp_sdc_rtc_time*)arg,
+                                   sizeof(struct hp_sdc_rtc_time)))
+                       return -EFAULT;
+
+                if (alm_tm.tm_hour > 23) return -EINVAL;
+               if (alm_tm.tm_min  > 59) return -EINVAL;
+               if (alm_tm.tm_sec  > 59) return -EINVAL;  
+
+               ttime.sec = alm_tm.tm_hour * 3600 + 
+                 alm_tm.tm_min * 60 + alm_tm.tm_sec;
+               ttime.usec = 0;
+               if (hp_sdc_rtc_set_mt(&ttime)) return -EFAULT;
+                return 0;
+        }
+        case RTC_RD_TIME:       /* Read the time/date from RTC  */
+        {
+               if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
+                break;
+        }
+        case RTC_SET_TIME:      /* Set the RTC */
+        {
+                struct rtc_time hp_sdc_rtc_tm;
+                unsigned char mon, day, hrs, min, sec, leap_yr;
+                unsigned int yrs;
+
+                if (!capable(CAP_SYS_TIME))
+                        return -EACCES;
+               if (copy_from_user(&hp_sdc_rtc_tm, (struct rtc_time *)arg,
+                                   sizeof(struct rtc_time)))
+                        return -EFAULT;
+
+                yrs = hp_sdc_rtc_tm.tm_year + 1900;
+                mon = hp_sdc_rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
+                day = hp_sdc_rtc_tm.tm_mday;
+                hrs = hp_sdc_rtc_tm.tm_hour;
+                min = hp_sdc_rtc_tm.tm_min;
+                sec = hp_sdc_rtc_tm.tm_sec;
+
+                if (yrs < 1970)
+                        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 -= eH) > 255)    /* They are unsigned */
+                        return -EINVAL;
+
+
+                return 0;
+        }
+        case RTC_EPOCH_READ:    /* Read the epoch.      */
+        {
+                return put_user (epoch, (unsigned long *)arg);
+        }
+        case RTC_EPOCH_SET:     /* Set the epoch.       */
+        {
+                /* 
+                 * There were no RTC clocks before 1900.
+                 */
+                if (arg < 1900)
+                 return -EINVAL;
+               if (!capable(CAP_SYS_TIME))
+                 return -EACCES;
+               
+                epoch = arg;
+                return 0;
+        }
+        default:
+                return -EINVAL;
+        }
+        return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
+#endif
+}
+
+static struct file_operations hp_sdc_rtc_fops = {
+        .owner =       THIS_MODULE,
+        .llseek =      hp_sdc_rtc_llseek,
+        .read =                hp_sdc_rtc_read,
+        .poll =                hp_sdc_rtc_poll,
+        .ioctl =       hp_sdc_rtc_ioctl,
+        .open =                hp_sdc_rtc_open,
+        .release =     hp_sdc_rtc_release,
+        .fasync =      hp_sdc_rtc_fasync,
+};
+
+static struct miscdevice hp_sdc_rtc_dev = {
+        .minor =       RTC_MINOR,
+        .name =                "rtc_HIL",
+        .fops =                &hp_sdc_rtc_fops
+};
+
+static int __init hp_sdc_rtc_init(void)
+{
+       int ret;
+
+       init_MUTEX(&i8042tregs);
+
+       if ((ret = hp_sdc_request_timer_irq(&hp_sdc_rtc_isr)))
+               return ret;
+       misc_register(&hp_sdc_rtc_dev);
+        create_proc_read_entry ("driver/rtc", 0, 0, 
+                               hp_sdc_rtc_read_proc, NULL);
+
+       printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support loaded "
+                        "(RTC v " RTC_VERSION ")\n");
+
+       return 0;
+}
+
+static void __exit hp_sdc_rtc_exit(void)
+{
+       remove_proc_entry ("driver/rtc", NULL);
+        misc_deregister(&hp_sdc_rtc_dev);
+       hp_sdc_release_timer_irq(hp_sdc_rtc_isr);
+        printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support unloaded\n");
+}
+
+module_init(hp_sdc_rtc_init);
+module_exit(hp_sdc_rtc_exit);
diff --git a/drivers/input/mouse/hil_ptr.c b/drivers/input/mouse/hil_ptr.c
new file mode 100644 (file)
index 0000000..bc22849
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Generic linux-input device driver for axis-bearing devices
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("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
+ *
+ * References:
+ * HP-HIL Technical Reference Manual.  Hewlett Packard Product No. 45918A
+ *
+ */
+
+#include <linux/hil.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci_ids.h>
+
+#define PREFIX "HIL PTR: "
+#define HIL_GENERIC_NAME "HIL pointer device"
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+
+#define TABLET_SIMULATES_MOUSE /* allow tablet to be used as mouse */
+#undef  TABLET_AUTOADJUST      /* auto-adjust valid tablet ranges */
+
+
+#define HIL_PTR_MAX_LENGTH 16
+
+struct hil_ptr {
+       struct input_dev dev;
+       struct serio *serio;
+
+       /* Input buffer and index for packets from HIL bus. */
+       hil_packet data[HIL_PTR_MAX_LENGTH];
+       int idx4; /* four counts per packet */
+
+       /* Raw device info records from HIL bus, see hil.h for fields. */
+       char    idd[HIL_PTR_MAX_LENGTH];        /* DID byte and IDD record */
+       char    rsc[HIL_PTR_MAX_LENGTH];        /* RSC record */
+       char    exd[HIL_PTR_MAX_LENGTH];        /* EXD record */
+       char    rnm[HIL_PTR_MAX_LENGTH + 1];    /* RNM record + NULL term. */
+
+       /* Extra device details not contained in struct input_dev. */
+       unsigned int nbtn, naxes;
+       unsigned int btnmap[7];
+
+       /* Something to sleep around with. */
+       struct semaphore sem;
+};
+
+/* Process a complete packet after transfer from the HIL */
+static void hil_ptr_process_record(struct hil_ptr *ptr)
+{
+       struct input_dev *dev = &ptr->dev;
+       hil_packet *data = ptr->data;
+       hil_packet p;
+       int idx, i, cnt, laxis;
+       int ax16, absdev;
+
+       idx = ptr->idx4/4;
+       p = data[idx - 1];
+
+       if ((p & ~HIL_CMDCT_POL) == 
+           (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) goto report;
+       if ((p & ~HIL_CMDCT_RPL) == 
+           (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) goto report;
+
+       /* Not a poll response.  See if we are loading config records. */
+       switch (p & HIL_PKT_DATA_MASK) {
+       case HIL_CMD_IDD:
+               for (i = 0; i < idx; i++)
+                       ptr->idd[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
+               for (; i < HIL_PTR_MAX_LENGTH; i++)
+                       ptr->idd[i] = 0;
+               break;
+       case HIL_CMD_RSC:
+               for (i = 0; i < idx; i++)
+                       ptr->rsc[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
+               for (; i < HIL_PTR_MAX_LENGTH; i++)
+                       ptr->rsc[i] = 0;
+               break;
+       case HIL_CMD_EXD:
+               for (i = 0; i < idx; i++)
+                       ptr->exd[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
+               for (; i < HIL_PTR_MAX_LENGTH; i++)
+                       ptr->exd[i] = 0;
+               break;
+       case HIL_CMD_RNM:
+               for (i = 0; i < idx; i++)
+                       ptr->rnm[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
+               for (; i < HIL_PTR_MAX_LENGTH + 1; i++)
+                       ptr->rnm[i] = '\0';
+               break;
+       default:
+               /* These occur when device isn't present */
+               if (p == (HIL_ERR_INT | HIL_PKT_CMD)) break; 
+               /* Anything else we'd like to know about. */
+               printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
+               break;
+       }
+       goto out;
+
+ report:
+       if ((p & HIL_CMDCT_POL) != idx - 1) {
+               printk(KERN_WARNING PREFIX "Malformed poll packet %x (idx = %i)\n", p, idx);
+               goto out;
+       }
+
+       i = (ptr->data[0] & HIL_POL_AXIS_ALT) ? 3 : 0;
+       laxis = ptr->data[0] & HIL_POL_NUM_AXES_MASK;
+       laxis += i;
+
+       ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */
+       absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS; 
+
+       for (cnt = 1; i < laxis; i++) {
+               unsigned int lo,hi,val;
+               lo = ptr->data[cnt++] & HIL_PKT_DATA_MASK;
+               hi = ax16 ? (ptr->data[cnt++] & HIL_PKT_DATA_MASK) : 0;
+               if (absdev) {
+                       val = lo + (hi<<8);
+#ifdef TABLET_AUTOADJUST
+                       if (val < ptr->dev.absmin[ABS_X + i])
+                               ptr->dev.absmin[ABS_X + i] = val;
+                       if (val > ptr->dev.absmax[ABS_X + i])
+                               ptr->dev.absmax[ABS_X + i] = val;
+#endif
+                       if (i%3) val = ptr->dev.absmax[ABS_X + i] - val;
+                       input_report_abs(dev, ABS_X + i, val);
+               } else {
+                       val = (int) (((int8_t)lo) | ((int8_t)hi<<8));
+                       if (i%3) val *= -1;
+                       input_report_rel(dev, REL_X + i, val);
+               }
+       }
+
+       while (cnt < idx - 1) {
+               unsigned int btn;
+               int up;
+               btn = ptr->data[cnt++];
+               up = btn & 1;
+               btn &= 0xfe;
+               if (btn == 0x8e) {
+                       continue; /* TODO: proximity == touch? */
+               }
+               else if ((btn > 0x8c) || (btn < 0x80)) continue;
+               btn = (btn - 0x80) >> 1;
+               btn = ptr->btnmap[btn];
+               input_report_key(dev, btn, !up);
+       }
+       input_sync(dev);
+ out:
+       ptr->idx4 = 0;
+       up(&ptr->sem);
+}
+
+static void hil_ptr_process_err(struct hil_ptr *ptr) {
+       printk(KERN_WARNING PREFIX "errored HIL packet\n");
+       ptr->idx4 = 0;
+       up(&ptr->sem);
+       return;
+}
+
+static irqreturn_t hil_ptr_interrupt(struct serio *serio, 
+        unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+       struct hil_ptr *ptr;
+       hil_packet packet;
+       int idx;
+
+       ptr = (struct hil_ptr *)serio->private;
+       if (ptr == NULL) {
+               BUG();
+               return IRQ_HANDLED;
+       }
+
+       if (ptr->idx4 >= (HIL_PTR_MAX_LENGTH * sizeof(hil_packet))) {
+               hil_ptr_process_err(ptr);
+               return IRQ_HANDLED;
+       }
+       idx = ptr->idx4/4;
+       if (!(ptr->idx4 % 4)) ptr->data[idx] = 0;
+       packet = ptr->data[idx];
+       packet |= ((hil_packet)data) << ((3 - (ptr->idx4 % 4)) * 8);
+       ptr->data[idx] = packet;
+
+       /* Records of N 4-byte hil_packets must terminate with a command. */
+       if ((++(ptr->idx4)) % 4) return IRQ_HANDLED;
+       if ((packet & 0xffff0000) != HIL_ERR_INT) {
+               hil_ptr_process_err(ptr);
+               return IRQ_HANDLED;
+       }
+       if (packet & HIL_PKT_CMD) 
+               hil_ptr_process_record(ptr);
+       return IRQ_HANDLED;
+}
+
+static void hil_ptr_disconnect(struct serio *serio)
+{
+       struct hil_ptr *ptr;
+
+       ptr = (struct hil_ptr *)serio->private;
+       if (ptr == NULL) {
+               BUG();
+               return;
+       }
+
+       input_unregister_device(&ptr->dev);
+       serio_close(serio);
+       kfree(ptr);
+}
+
+static void hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
+{
+       struct hil_ptr  *ptr;
+       char            *txt;
+       unsigned int    i, naxsets, btntype;
+       uint8_t         did, *idd;
+
+       if (serio->type != (SERIO_HIL_MLC | SERIO_HIL)) return;
+
+       if (!(ptr = kmalloc(sizeof(struct hil_ptr), GFP_KERNEL))) return;
+       memset(ptr, 0, sizeof(struct hil_ptr));
+
+       if (serio_open(serio, driver)) goto bail0;
+
+       serio->private = ptr;
+       ptr->serio = serio;
+       ptr->dev.private = ptr;
+
+       init_MUTEX_LOCKED(&(ptr->sem));
+
+       /* Get device info.  MLC driver supplies devid/status/etc. */
+       serio->write(serio, 0);
+       serio->write(serio, 0);
+       serio->write(serio, HIL_PKT_CMD >> 8);
+       serio->write(serio, HIL_CMD_IDD);
+       down(&(ptr->sem));
+
+       serio->write(serio, 0);
+       serio->write(serio, 0);
+       serio->write(serio, HIL_PKT_CMD >> 8);
+       serio->write(serio, HIL_CMD_RSC);
+       down(&(ptr->sem));
+
+       serio->write(serio, 0);
+       serio->write(serio, 0);
+       serio->write(serio, HIL_PKT_CMD >> 8);
+       serio->write(serio, HIL_CMD_RNM);
+       down(&(ptr->sem));
+
+       serio->write(serio, 0);
+       serio->write(serio, 0);
+       serio->write(serio, HIL_PKT_CMD >> 8);
+       serio->write(serio, HIL_CMD_EXD);
+       down(&(ptr->sem));
+
+       up(&(ptr->sem));
+
+       init_input_dev(&ptr->dev);
+       did = ptr->idd[0];
+       idd = ptr->idd + 1;
+       txt = "unknown";
+       if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) {
+               ptr->dev.evbit[0] = BIT(EV_REL);
+               txt = "relative";
+       }
+
+       if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_ABS) {
+               ptr->dev.evbit[0] = BIT(EV_ABS);
+               txt = "absolute";
+       }
+       if (!ptr->dev.evbit[0]) {
+               goto bail1;
+       }
+
+       ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd);
+       if (ptr->nbtn) ptr->dev.evbit[0] |= BIT(EV_KEY);
+
+       naxsets = HIL_IDD_NUM_AXSETS(*idd);
+       ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd);
+
+       printk(KERN_INFO PREFIX "HIL pointer device found (did: 0x%02x, axis: %s)\n",
+                       did, txt);
+       printk(KERN_INFO PREFIX "HIL pointer has %i buttons and %i sets of %i axes\n",
+                       ptr->nbtn, naxsets, ptr->naxes);
+       
+       btntype = BTN_MISC;
+       if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET)
+#ifdef TABLET_SIMULATES_MOUSE
+               btntype = BTN_TOUCH;
+#else
+               btntype = BTN_DIGI;
+#endif
+       if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN)
+               btntype = BTN_TOUCH;
+               
+       if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE)
+               btntype = BTN_MOUSE;
+
+       for (i = 0; i < ptr->nbtn; i++) {
+               set_bit(btntype | i, ptr->dev.keybit);
+               ptr->btnmap[i] = btntype | i;
+       }
+
+       if (btntype == BTN_MOUSE) {
+               /* Swap buttons 2 and 3 */
+               ptr->btnmap[1] = BTN_MIDDLE;
+               ptr->btnmap[2] = BTN_RIGHT;
+       }
+
+       if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) {
+               for (i = 0; i < ptr->naxes; i++) {
+                       set_bit(REL_X + i, ptr->dev.relbit);
+               }
+               for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) {
+                       set_bit(REL_X + i, ptr->dev.relbit);
+               }
+       } else {
+               for (i = 0; i < ptr->naxes; i++) {
+                       set_bit(ABS_X + i, ptr->dev.absbit);
+                       ptr->dev.absmin[ABS_X + i] = 0;
+                       ptr->dev.absmax[ABS_X + i] = 
+                               HIL_IDD_AXIS_MAX((ptr->idd + 1), i);
+               }
+               for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) {
+                       set_bit(ABS_X + i, ptr->dev.absbit);
+                       ptr->dev.absmin[ABS_X + i] = 0;
+                       ptr->dev.absmax[ABS_X + i] = 
+                               HIL_IDD_AXIS_MAX((ptr->idd + 1), (i - 3));
+               }
+#ifdef TABLET_AUTOADJUST
+               for (i = 0; i < ABS_MAX; i++) {
+                       int diff = ptr->dev.absmax[ABS_X + i] / 10;
+                       ptr->dev.absmin[ABS_X + i] += diff;
+                       ptr->dev.absmax[ABS_X + i] -= diff;
+               }
+#endif
+       }
+
+       ptr->dev.name = strlen(ptr->rnm) ? ptr->rnm : HIL_GENERIC_NAME;
+
+       ptr->dev.id.bustype     = BUS_HIL;
+       ptr->dev.id.vendor      = PCI_VENDOR_ID_HP;
+       ptr->dev.id.product     = 0x0001; /* TODO: get from ptr->rsc */
+       ptr->dev.id.version     = 0x0100; /* TODO: get from ptr->rsc */
+       ptr->dev.dev            = &serio->dev;
+
+       input_register_device(&ptr->dev);
+       printk(KERN_INFO "input: %s (%s), ID: %d\n",
+                ptr->dev.name, 
+               (btntype == BTN_MOUSE) ? "HIL mouse":"HIL tablet or touchpad",
+               did);
+
+       return;
+ bail1:
+       serio_close(serio);
+ bail0:
+       kfree(ptr);
+       return;
+}
+
+
+static struct serio_driver hil_ptr_serio_driver = {
+       .driver         = {
+               .name   = "hil_ptr",
+       },
+       .description    = "HP HIL mouse/tablet driver",
+       .connect =      hil_ptr_connect,
+       .disconnect =   hil_ptr_disconnect,
+       .interrupt =    hil_ptr_interrupt
+};
+
+static int __init hil_ptr_init(void)
+{
+       serio_register_driver(&hil_ptr_serio_driver);
+        return 0;
+}
+                
+static void __exit hil_ptr_exit(void)
+{
+       serio_unregister_driver(&hil_ptr_serio_driver);
+}
+                        
+module_init(hil_ptr_init);
+module_exit(hil_ptr_exit);
diff --git a/drivers/input/serio/hil_mlc.c b/drivers/input/serio/hil_mlc.c
new file mode 100644 (file)
index 0000000..c243cb6
--- /dev/null
@@ -0,0 +1,949 @@
+/*
+ * HIL MLC state machine and serio interface driver
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("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
+ *
+ * References:
+ * HP-HIL Technical Reference Manual.  Hewlett Packard Product No. 45918A
+ *
+ *
+ *     Driver theory of operation:
+ *
+ *     Some access methods and an ISR is defined by the sub-driver 
+ *     (e.g. hp_sdc_mlc.c).  These methods are expected to provide a 
+ *     few bits of logic in addition to raw access to the HIL MLC, 
+ *     specifically, the ISR, which is entirely registered by the 
+ *     sub-driver and invoked directly, must check for record 
+ *     termination or packet match, at which point a semaphore must
+ *     be cleared and then the hil_mlcs_tasklet must be scheduled.
+ *
+ *     The hil_mlcs_tasklet processes the state machine for all MLCs
+ *     each time it runs, checking each MLC's progress at the current
+ *     node in the state machine, and moving the MLC to subsequent nodes
+ *     in the state machine when appropriate.  It will reschedule
+ *     itself if output is pending.  (This rescheduling should be replaced
+ *     at some point with a sub-driver-specific mechanism.)
+ *
+ *     A timer task prods the tasklet once per second to prevent 
+ *     hangups when attached devices do not return expected data
+ *     and to initiate probes of the loop for new devices.
+ */
+
+#include <linux/hil_mlc.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("HIL MLC serio");
+MODULE_LICENSE("Dual BSD/GPL");
+
+EXPORT_SYMBOL(hil_mlc_register);
+EXPORT_SYMBOL(hil_mlc_unregister);
+
+#define PREFIX "HIL MLC: "
+
+static LIST_HEAD(hil_mlcs);
+static DEFINE_RWLOCK(hil_mlcs_lock);
+static struct timer_list       hil_mlcs_kicker;
+static int                     hil_mlcs_probe;
+
+static void hil_mlcs_process(unsigned long unused);
+DECLARE_TASKLET_DISABLED(hil_mlcs_tasklet, hil_mlcs_process, 0);
+
+
+/* #define HIL_MLC_DEBUG */
+
+/********************** Device info/instance management **********************/
+
+static void hil_mlc_clear_di_map (hil_mlc *mlc, int val) {
+       int j;
+       for (j = val; j < 7 ; j++) {
+               mlc->di_map[j] = -1;
+       }
+}
+
+static void hil_mlc_clear_di_scratch (hil_mlc *mlc) {
+       memset(&(mlc->di_scratch), 0, sizeof(mlc->di_scratch));
+}
+
+static void hil_mlc_copy_di_scratch (hil_mlc *mlc, int idx) {
+       memcpy(&(mlc->di[idx]), &(mlc->di_scratch), sizeof(mlc->di_scratch));
+}
+
+static int hil_mlc_match_di_scratch (hil_mlc *mlc) {
+       int idx;
+
+       for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
+               int j, found;
+
+               /* In-use slots are not eligible. */
+               found = 0;
+               for (j = 0; j < 7 ; j++) {
+                       if (mlc->di_map[j] == idx) found++;
+               }
+               if (found) continue;
+               if (!memcmp(mlc->di + idx, 
+                           &(mlc->di_scratch), 
+                           sizeof(mlc->di_scratch))) break;
+       }
+       return((idx >= HIL_MLC_DEVMEM) ? -1 : idx);
+}
+
+static int hil_mlc_find_free_di(hil_mlc *mlc) {
+       int idx;
+       /* TODO: Pick all-zero slots first, failing that, 
+        * randomize the slot picked among those eligible. 
+        */
+       for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
+               int j, found;
+               found = 0;
+               for (j = 0; j < 7 ; j++) {
+                       if (mlc->di_map[j] == idx) found++;
+               }
+               if (!found) break;
+       }
+       return(idx); /* Note: It is guaranteed at least one above will match */
+}
+
+static inline void hil_mlc_clean_serio_map(hil_mlc *mlc) {
+       int idx;
+       for (idx = 0; idx < HIL_MLC_DEVMEM; idx++) {
+               int j, found;
+               found = 0;
+               for (j = 0; j < 7 ; j++) {
+                       if (mlc->di_map[j] == idx) found++;
+               }
+               if (!found) mlc->serio_map[idx].di_revmap = -1;
+       }
+}
+
+static void hil_mlc_send_polls(hil_mlc *mlc) {
+       int did, i, cnt;
+       struct serio *serio;
+       struct serio_driver *drv;
+
+       i = cnt = 0;
+       did = (mlc->ipacket[0] & HIL_PKT_ADDR_MASK) >> 8;
+       serio = did ? mlc->serio[mlc->di_map[did - 1]] : NULL;
+       drv = (serio != NULL) ? serio->drv : NULL;
+
+       while (mlc->icount < 15 - i) {
+               hil_packet p;
+               p = mlc->ipacket[i];
+               if (did != (p & HIL_PKT_ADDR_MASK) >> 8) {
+                       if (drv == NULL || drv->interrupt == NULL) goto skip;
+
+                       drv->interrupt(serio, 0, 0, NULL);
+                       drv->interrupt(serio, HIL_ERR_INT >> 16, 0, NULL);
+                       drv->interrupt(serio, HIL_PKT_CMD >> 8,  0, NULL);
+                       drv->interrupt(serio, HIL_CMD_POL + cnt, 0, NULL);
+               skip:
+                       did = (p & HIL_PKT_ADDR_MASK) >> 8;
+                       serio = did ? mlc->serio[mlc->di_map[did-1]] : NULL;
+                       drv = (serio != NULL) ? serio->drv : NULL;
+                       cnt = 0;
+               }
+               cnt++; i++;
+               if (drv == NULL || drv->interrupt == NULL) continue;
+               drv->interrupt(serio, (p >> 24), 0, NULL);
+               drv->interrupt(serio, (p >> 16) & 0xff, 0, NULL);
+               drv->interrupt(serio, (p >> 8) & ~HIL_PKT_ADDR_MASK, 0, NULL);
+               drv->interrupt(serio, p & 0xff, 0, NULL);
+       }
+}
+
+/*************************** State engine *********************************/
+
+#define HILSEN_SCHED   0x000100        /* Schedule the tasklet         */
+#define HILSEN_BREAK   0x000200        /* Wait until next pass         */
+#define HILSEN_UP      0x000400        /* relative node#, decrement    */
+#define HILSEN_DOWN    0x000800        /* relative node#, increment    */
+#define HILSEN_FOLLOW  0x001000        /* use retval as next node#     */
+
+#define HILSEN_MASK    0x0000ff
+#define HILSEN_START   0
+#define HILSEN_RESTART 1
+#define HILSEN_DHR     9
+#define HILSEN_DHR2    10
+#define HILSEN_IFC     14
+#define HILSEN_HEAL0   16
+#define HILSEN_HEAL    18
+#define HILSEN_ACF      21
+#define HILSEN_ACF2    22
+#define HILSEN_DISC0   25
+#define HILSEN_DISC    27
+#define HILSEN_MATCH   40
+#define HILSEN_OPERATE 41
+#define HILSEN_PROBE   44
+#define HILSEN_DSR     52
+#define HILSEN_REPOLL  55
+#define HILSEN_IFCACF  58
+#define HILSEN_END     60
+
+#define HILSEN_NEXT    (HILSEN_DOWN | 1)
+#define HILSEN_SAME    (HILSEN_DOWN | 0)
+#define HILSEN_LAST    (HILSEN_UP | 1)
+
+#define HILSEN_DOZE    (HILSEN_SAME | HILSEN_SCHED | HILSEN_BREAK)
+#define HILSEN_SLEEP   (HILSEN_SAME | HILSEN_BREAK)
+
+static int hilse_match(hil_mlc *mlc, int unused) {
+       int rc;
+       rc = hil_mlc_match_di_scratch(mlc);
+       if (rc == -1) {
+               rc = hil_mlc_find_free_di(mlc);
+               if (rc == -1) goto err;
+#ifdef HIL_MLC_DEBUG
+               printk(KERN_DEBUG PREFIX "new in slot %i\n", rc);
+#endif
+               hil_mlc_copy_di_scratch(mlc, rc);
+               mlc->di_map[mlc->ddi] = rc;
+               mlc->serio_map[rc].di_revmap = mlc->ddi;
+               hil_mlc_clean_serio_map(mlc);
+               serio_rescan(mlc->serio[rc]);
+               return -1;
+       }
+       mlc->di_map[mlc->ddi] = rc;
+#ifdef HIL_MLC_DEBUG
+       printk(KERN_DEBUG PREFIX "same in slot %i\n", rc);
+#endif
+       mlc->serio_map[rc].di_revmap = mlc->ddi;
+       hil_mlc_clean_serio_map(mlc);
+       return 0;
+ err:
+       printk(KERN_ERR PREFIX "Residual device slots exhausted, close some serios!\n");
+       return 1;
+}
+
+/* An LCV used to prevent runaway loops, forces 5 second sleep when reset. */
+static int hilse_init_lcv(hil_mlc *mlc, int unused) {
+       struct timeval tv;
+
+       do_gettimeofday(&tv);
+
+       if(mlc->lcv == 0) goto restart;  /* First init, no need to dally */
+       if(tv.tv_sec - mlc->lcv_tv.tv_sec < 5) return -1;
+ restart:
+       mlc->lcv_tv = tv;
+       mlc->lcv = 0;
+       return 0;
+}
+
+static int hilse_inc_lcv(hil_mlc *mlc, int lim) {
+       if (mlc->lcv++ >= lim) return -1;
+       return 0;
+}
+
+#if 0
+static int hilse_set_lcv(hil_mlc *mlc, int val) {
+       mlc->lcv = val;
+       return 0;
+}
+#endif
+
+/* Management of the discovered device index (zero based, -1 means no devs) */
+static int hilse_set_ddi(hil_mlc *mlc, int val) {
+       mlc->ddi = val;
+       hil_mlc_clear_di_map(mlc, val + 1);
+       return 0;
+}
+
+static int hilse_dec_ddi(hil_mlc *mlc, int unused) {
+       mlc->ddi--;
+       if (mlc->ddi <= -1) { 
+               mlc->ddi = -1;
+               hil_mlc_clear_di_map(mlc, 0);
+               return -1;
+       }
+       hil_mlc_clear_di_map(mlc, mlc->ddi + 1);
+       return 0;
+}
+
+static int hilse_inc_ddi(hil_mlc *mlc, int unused) {
+       if (mlc->ddi >= 6) {
+               BUG();
+               return -1;
+       }
+       mlc->ddi++;
+       return 0;
+}
+
+static int hilse_take_idd(hil_mlc *mlc, int unused) {
+       int i;
+
+       /* Help the state engine: 
+        * Is this a real IDD response or just an echo? 
+        *
+        * Real IDD response does not start with a command. 
+        */
+       if (mlc->ipacket[0] & HIL_PKT_CMD) goto bail;
+       /* Should have the command echoed further down. */
+       for (i = 1; i < 16; i++) {
+               if (((mlc->ipacket[i] & HIL_PKT_ADDR_MASK) == 
+                    (mlc->ipacket[0] & HIL_PKT_ADDR_MASK)) &&
+                   (mlc->ipacket[i] & HIL_PKT_CMD) && 
+                   ((mlc->ipacket[i] & HIL_PKT_DATA_MASK) == HIL_CMD_IDD))
+                       break;
+       }
+       if (i > 15) goto bail;
+       /* And the rest of the packets should still be clear. */
+       while (++i < 16) {
+               if (mlc->ipacket[i]) break;
+       }
+       if (i < 16) goto bail;
+       for (i = 0; i < 16; i++) {
+               mlc->di_scratch.idd[i] = 
+                       mlc->ipacket[i] & HIL_PKT_DATA_MASK;
+       }
+       /* Next step is to see if RSC supported */
+       if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_RSC) 
+               return HILSEN_NEXT;
+       if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) 
+               return HILSEN_DOWN | 4;
+       return 0;
+ bail:
+       mlc->ddi--;
+       return -1; /* This should send us off to ACF */
+}
+
+static int hilse_take_rsc(hil_mlc *mlc, int unused) {
+       int i;
+
+       for (i = 0; i < 16; i++) {
+               mlc->di_scratch.rsc[i] = 
+                       mlc->ipacket[i] & HIL_PKT_DATA_MASK;
+       }
+       /* Next step is to see if EXD supported (IDD has already been read) */
+       if (mlc->di_scratch.idd[1] & HIL_IDD_HEADER_EXD) 
+               return HILSEN_NEXT;
+       return 0;
+}
+
+static int hilse_take_exd(hil_mlc *mlc, int unused) {
+       int i;
+
+       for (i = 0; i < 16; i++) {
+               mlc->di_scratch.exd[i] = 
+                       mlc->ipacket[i] & HIL_PKT_DATA_MASK;
+       }
+       /* Next step is to see if RNM supported. */
+       if (mlc->di_scratch.exd[0] & HIL_EXD_HEADER_RNM) 
+               return HILSEN_NEXT;
+       return 0;
+}
+
+static int hilse_take_rnm(hil_mlc *mlc, int unused) {
+       int i;
+
+       for (i = 0; i < 16; i++) {
+               mlc->di_scratch.rnm[i] = 
+                       mlc->ipacket[i] & HIL_PKT_DATA_MASK;
+       }
+       do {
+         char nam[17];
+         snprintf(nam, 16, "%s", mlc->di_scratch.rnm);
+         nam[16] = '\0';
+         printk(KERN_INFO PREFIX "Device name gotten: %s\n", nam);
+       } while (0);
+       return 0;
+}
+
+static int hilse_operate(hil_mlc *mlc, int repoll) { 
+
+       if (mlc->opercnt == 0) hil_mlcs_probe = 0;
+       mlc->opercnt = 1;
+
+       hil_mlc_send_polls(mlc);
+
+       if (!hil_mlcs_probe) return 0;
+       hil_mlcs_probe = 0;
+       mlc->opercnt = 0;
+       return 1;
+}
+
+#define FUNC(funct, funct_arg, zero_rc, neg_rc, pos_rc) \
+{ HILSE_FUNC,          { func: &funct }, funct_arg, zero_rc, neg_rc, pos_rc },
+#define OUT(pack) \
+{ HILSE_OUT,           { packet: pack }, 0, HILSEN_NEXT, HILSEN_DOZE, 0 },
+#define CTS \
+{ HILSE_CTS,           { packet: 0    }, 0, HILSEN_NEXT | HILSEN_SCHED | HILSEN_BREAK, HILSEN_DOZE, 0 },
+#define EXPECT(comp, to, got, got_wrong, timed_out) \
+{ HILSE_EXPECT,                { packet: comp }, to, got, got_wrong, timed_out },
+#define EXPECT_LAST(comp, to, got, got_wrong, timed_out) \
+{ HILSE_EXPECT_LAST,   { packet: comp }, to, got, got_wrong, timed_out },
+#define EXPECT_DISC(comp, to, got, got_wrong, timed_out) \
+{ HILSE_EXPECT_DISC,   { packet: comp }, to, got, got_wrong, timed_out },
+#define IN(to, got, got_error, timed_out) \
+{ HILSE_IN,            { packet: 0    }, to, got, got_error, timed_out },
+#define OUT_DISC(pack) \
+{ HILSE_OUT_DISC,      { packet: pack }, 0, 0, 0, 0 },
+#define OUT_LAST(pack) \
+{ HILSE_OUT_LAST,      { packet: pack }, 0, 0, 0, 0 },
+
+struct hilse_node hil_mlc_se[HILSEN_END] = {
+
+       /* 0  HILSEN_START */
+       FUNC(hilse_init_lcv, 0, HILSEN_NEXT,    HILSEN_SLEEP,   0)
+
+       /* 1  HILSEN_RESTART */
+       FUNC(hilse_inc_lcv, 10, HILSEN_NEXT,    HILSEN_START,  0)
+       OUT(HIL_CTRL_ONLY)                      /* Disable APE */
+       CTS
+
+#define TEST_PACKET(x) \
+(HIL_PKT_CMD | (x << HIL_PKT_ADDR_SHIFT) | x << 4 | x)
+
+       OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0x5))
+       EXPECT(HIL_ERR_INT | TEST_PACKET(0x5),
+              2000,            HILSEN_NEXT,    HILSEN_RESTART, HILSEN_RESTART)
+       OUT(HIL_DO_ALTER_CTRL | HIL_CTRL_TEST | TEST_PACKET(0xa))
+       EXPECT(HIL_ERR_INT | TEST_PACKET(0xa),
+              2000,            HILSEN_NEXT,    HILSEN_RESTART, HILSEN_RESTART)
+       OUT(HIL_CTRL_ONLY | 0)                  /* Disable test mode */
+       
+       /* 9  HILSEN_DHR */
+       FUNC(hilse_init_lcv, 0, HILSEN_NEXT,    HILSEN_SLEEP,   0)
+
+       /* 10 HILSEN_DHR2 */
+       FUNC(hilse_inc_lcv, 10, HILSEN_NEXT,    HILSEN_START,   0)
+       FUNC(hilse_set_ddi, -1, HILSEN_NEXT,    0,              0)
+       OUT(HIL_PKT_CMD | HIL_CMD_DHR)
+       IN(300000,              HILSEN_DHR2,    HILSEN_DHR2,    HILSEN_NEXT)
+
+       /* 14 HILSEN_IFC */
+       OUT(HIL_PKT_CMD | HIL_CMD_IFC)
+       EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT,
+              20000,           HILSEN_DISC,    HILSEN_DHR2,    HILSEN_NEXT )
+
+       /* If devices are there, they weren't in PUP or other loopback mode.
+        * We're more concerned at this point with restoring operation
+        * to devices than discovering new ones, so we try to salvage
+        * the loop configuration by closing off the loop.
+        */
+
+       /* 16 HILSEN_HEAL0 */
+       FUNC(hilse_dec_ddi, 0,  HILSEN_NEXT,    HILSEN_ACF,     0)
+       FUNC(hilse_inc_ddi, 0,  HILSEN_NEXT,    0,              0)
+
+       /* 18 HILSEN_HEAL */
+       OUT_LAST(HIL_CMD_ELB)
+       EXPECT_LAST(HIL_CMD_ELB | HIL_ERR_INT, 
+                   20000,      HILSEN_REPOLL,  HILSEN_DSR,     HILSEN_NEXT)
+       FUNC(hilse_dec_ddi, 0,  HILSEN_HEAL,    HILSEN_NEXT,    0)
+
+       /* 21 HILSEN_ACF */
+       FUNC(hilse_init_lcv, 0, HILSEN_NEXT,    HILSEN_DOZE,    0)
+
+       /* 22 HILSEN_ACF2 */
+       FUNC(hilse_inc_lcv, 10, HILSEN_NEXT,    HILSEN_START,   0)
+       OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1)
+       IN(20000,               HILSEN_NEXT,    HILSEN_DSR,     HILSEN_NEXT)
+
+       /* 25 HILSEN_DISC0 */
+       OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB)
+       EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_ELB | HIL_ERR_INT,
+              20000,           HILSEN_NEXT,    HILSEN_DSR,     HILSEN_DSR)
+
+       /* Only enter here if response just received */
+       /* 27 HILSEN_DISC */
+       OUT_DISC(HIL_PKT_CMD | HIL_CMD_IDD)
+       EXPECT_DISC(HIL_PKT_CMD | HIL_CMD_IDD | HIL_ERR_INT,
+              20000,           HILSEN_NEXT,    HILSEN_DSR,     HILSEN_START)
+       FUNC(hilse_inc_ddi,  0, HILSEN_NEXT,    HILSEN_START,   0)
+       FUNC(hilse_take_idd, 0, HILSEN_MATCH,   HILSEN_IFCACF,  HILSEN_FOLLOW)
+       OUT_LAST(HIL_PKT_CMD | HIL_CMD_RSC)
+       EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RSC | HIL_ERR_INT,
+              30000,           HILSEN_NEXT,    HILSEN_DSR,     HILSEN_DSR)
+       FUNC(hilse_take_rsc, 0, HILSEN_MATCH,   0,              HILSEN_FOLLOW)
+       OUT_LAST(HIL_PKT_CMD | HIL_CMD_EXD)
+       EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_EXD | HIL_ERR_INT,
+              30000,           HILSEN_NEXT,    HILSEN_DSR,     HILSEN_DSR)
+       FUNC(hilse_take_exd, 0, HILSEN_MATCH,   0,              HILSEN_FOLLOW)
+       OUT_LAST(HIL_PKT_CMD | HIL_CMD_RNM)
+       EXPECT_LAST(HIL_PKT_CMD | HIL_CMD_RNM | HIL_ERR_INT,
+              30000,           HILSEN_NEXT,    HILSEN_DSR,     HILSEN_DSR)
+       FUNC(hilse_take_rnm, 0, HILSEN_MATCH,   0,              0)
+
+       /* 40 HILSEN_MATCH */
+       FUNC(hilse_match, 0,    HILSEN_NEXT,    HILSEN_NEXT,    /* TODO */ 0)
+
+       /* 41 HILSEN_OPERATE */
+       OUT(HIL_PKT_CMD | HIL_CMD_POL)
+       EXPECT(HIL_PKT_CMD | HIL_CMD_POL | HIL_ERR_INT,
+              20000,           HILSEN_NEXT,    HILSEN_DSR,     HILSEN_NEXT)
+       FUNC(hilse_operate, 0,  HILSEN_OPERATE, HILSEN_IFC,     HILSEN_NEXT)
+
+       /* 44 HILSEN_PROBE */
+       OUT_LAST(HIL_PKT_CMD | HIL_CMD_EPT)
+       IN(10000,               HILSEN_DISC,    HILSEN_DSR,     HILSEN_NEXT)
+       OUT_DISC(HIL_PKT_CMD | HIL_CMD_ELB)
+       IN(10000,               HILSEN_DISC,    HILSEN_DSR,     HILSEN_NEXT)
+       OUT(HIL_PKT_CMD | HIL_CMD_ACF | 1)
+       IN(10000,               HILSEN_DISC0,   HILSEN_DSR,     HILSEN_NEXT)
+       OUT_LAST(HIL_PKT_CMD | HIL_CMD_ELB)
+       IN(10000,               HILSEN_OPERATE, HILSEN_DSR,     HILSEN_DSR)
+
+       /* 52 HILSEN_DSR */
+       FUNC(hilse_set_ddi, -1, HILSEN_NEXT,    0,              0)
+       OUT(HIL_PKT_CMD | HIL_CMD_DSR)
+       IN(20000,               HILSEN_DHR,     HILSEN_DHR,     HILSEN_IFC)
+
+       /* 55 HILSEN_REPOLL */
+       OUT(HIL_PKT_CMD | HIL_CMD_RPL)
+       EXPECT(HIL_PKT_CMD | HIL_CMD_RPL | HIL_ERR_INT,
+              20000,           HILSEN_NEXT,    HILSEN_DSR,     HILSEN_NEXT)
+       FUNC(hilse_operate, 1,  HILSEN_OPERATE, HILSEN_IFC,     HILSEN_PROBE)
+
+       /* 58 HILSEN_IFCACF */
+       OUT(HIL_PKT_CMD | HIL_CMD_IFC)
+       EXPECT(HIL_PKT_CMD | HIL_CMD_IFC | HIL_ERR_INT,
+              20000,           HILSEN_ACF2,    HILSEN_DHR2,    HILSEN_HEAL)
+
+       /* 60 HILSEN_END */
+};
+
+static inline void hilse_setup_input(hil_mlc *mlc, struct hilse_node *node) {
+
+       switch (node->act) {
+       case HILSE_EXPECT_DISC:
+               mlc->imatch = node->object.packet;
+               mlc->imatch |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT);
+               break;
+       case HILSE_EXPECT_LAST:
+               mlc->imatch = node->object.packet;
+               mlc->imatch |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT);
+               break;
+       case HILSE_EXPECT:
+               mlc->imatch = node->object.packet;
+               break;
+       case HILSE_IN:
+               mlc->imatch = 0;
+               break;
+       default:
+               BUG();
+       }
+       mlc->istarted = 1;
+       mlc->intimeout = node->arg;
+       do_gettimeofday(&(mlc->instart));
+       mlc->icount = 15;
+       memset(mlc->ipacket, 0, 16 * sizeof(hil_packet));
+       if (down_trylock(&(mlc->isem))) BUG();
+
+       return;
+}
+
+#ifdef HIL_MLC_DEBUG
+static int doze = 0;
+static int seidx; /* For debug */
+static int kick = 1;
+#endif
+
+static int hilse_donode (hil_mlc *mlc) {
+       struct hilse_node *node;
+       int nextidx = 0;
+       int sched_long = 0;
+       unsigned long flags;
+
+#ifdef HIL_MLC_DEBUG
+       if (mlc->seidx && (mlc->seidx != seidx)  && mlc->seidx != 41 && mlc->seidx != 42 && mlc->seidx != 43) {
+         printk(KERN_DEBUG PREFIX "z%i \n%s {%i}", doze, kick ? "K" : "", mlc->seidx);
+               doze = 0;
+       }
+       kick = 0;
+
+       seidx = mlc->seidx;
+#endif
+       node = hil_mlc_se + mlc->seidx;
+
+       switch (node->act) {
+               int rc;
+               hil_packet pack;
+
+       case HILSE_FUNC:
+               if (node->object.func == NULL) break;
+               rc = node->object.func(mlc, node->arg);
+               nextidx = (rc > 0) ? node->ugly : 
+                       ((rc < 0) ? node->bad : node->good);
+               if (nextidx == HILSEN_FOLLOW) nextidx = rc;
+               break;
+       case HILSE_EXPECT_LAST:
+       case HILSE_EXPECT_DISC:
+       case HILSE_EXPECT:
+       case HILSE_IN:
+               /* Already set up from previous HILSE_OUT_* */
+               write_lock_irqsave(&(mlc->lock), flags);
+               rc = mlc->in(mlc, node->arg);
+               if (rc == 2)  {
+                       nextidx = HILSEN_DOZE;
+                       sched_long = 1;
+                       write_unlock_irqrestore(&(mlc->lock), flags);
+                       break;
+               }
+               if (rc == 1)            nextidx = node->ugly;
+               else if (rc == 0)       nextidx = node->good;
+               else                    nextidx = node->bad;
+               mlc->istarted = 0;
+               write_unlock_irqrestore(&(mlc->lock), flags);
+               break;
+       case HILSE_OUT_LAST:
+               write_lock_irqsave(&(mlc->lock), flags);
+               pack = node->object.packet;
+               pack |= ((mlc->ddi + 1) << HIL_PKT_ADDR_SHIFT);
+               goto out;
+       case HILSE_OUT_DISC:
+               write_lock_irqsave(&(mlc->lock), flags);
+               pack = node->object.packet;
+               pack |= ((mlc->ddi + 2) << HIL_PKT_ADDR_SHIFT);
+               goto out;
+       case HILSE_OUT:
+               write_lock_irqsave(&(mlc->lock), flags);
+               pack = node->object.packet;
+       out:
+               if (mlc->istarted) goto out2;
+               /* Prepare to receive input */
+               if ((node + 1)->act & HILSE_IN)
+                       hilse_setup_input(mlc, node + 1);
+
+       out2:
+               write_unlock_irqrestore(&(mlc->lock), flags);
+
+               if (down_trylock(&mlc->osem)) {
+                       nextidx = HILSEN_DOZE;
+                       break;
+               }
+               up(&mlc->osem);
+
+               write_lock_irqsave(&(mlc->lock), flags);
+               if (!(mlc->ostarted)) {
+                       mlc->ostarted = 1;
+                       mlc->opacket = pack;
+                       mlc->out(mlc);
+                       nextidx = HILSEN_DOZE;
+                       write_unlock_irqrestore(&(mlc->lock), flags);
+                       break;
+               }
+               mlc->ostarted = 0;
+               do_gettimeofday(&(mlc->instart));
+               write_unlock_irqrestore(&(mlc->lock), flags);
+               nextidx = HILSEN_NEXT;
+               break;
+       case HILSE_CTS:
+               nextidx = mlc->cts(mlc) ? node->bad : node->good;
+               break;
+       default:
+               BUG();
+               nextidx = 0;
+               break;
+       }
+
+#ifdef HIL_MLC_DEBUG
+       if (nextidx == HILSEN_DOZE) doze++;
+#endif
+
+       while (nextidx & HILSEN_SCHED) {
+               struct timeval tv;
+
+               if (!sched_long) goto sched;
+
+               do_gettimeofday(&tv);
+               tv.tv_usec += 1000000 * (tv.tv_sec - mlc->instart.tv_sec);
+               tv.tv_usec -= mlc->instart.tv_usec;
+               if (tv.tv_usec >= mlc->intimeout) goto sched;
+               tv.tv_usec = (mlc->intimeout - tv.tv_usec) * HZ / 1000000;
+               if (!tv.tv_usec) goto sched;
+               mod_timer(&hil_mlcs_kicker, jiffies + tv.tv_usec);
+               break;
+       sched:
+               tasklet_schedule(&hil_mlcs_tasklet);
+               break;
+       } 
+       if (nextidx & HILSEN_DOWN) mlc->seidx += nextidx & HILSEN_MASK;
+       else if (nextidx & HILSEN_UP) mlc->seidx -= nextidx & HILSEN_MASK;
+       else mlc->seidx = nextidx & HILSEN_MASK;
+
+       if (nextidx & HILSEN_BREAK)     return 1;
+       return 0;
+}
+
+/******************** tasklet context functions **************************/
+static void hil_mlcs_process(unsigned long unused) {
+       struct list_head *tmp;
+
+       read_lock(&hil_mlcs_lock);
+       list_for_each(tmp, &hil_mlcs) {
+               struct hil_mlc *mlc = list_entry(tmp, hil_mlc, list);
+               while (hilse_donode(mlc) == 0) {
+#ifdef HIL_MLC_DEBUG
+                 if (mlc->seidx != 41 && 
+                     mlc->seidx != 42 && 
+                     mlc->seidx != 43) 
+                   printk(KERN_DEBUG PREFIX " + ");
+#endif
+               };
+       }
+       read_unlock(&hil_mlcs_lock);
+}
+
+/************************* Keepalive timer task *********************/
+
+void hil_mlcs_timer (unsigned long data) {
+       hil_mlcs_probe = 1;
+       tasklet_schedule(&hil_mlcs_tasklet);
+       /* Re-insert the periodic task. */
+       if (!timer_pending(&hil_mlcs_kicker))
+               mod_timer(&hil_mlcs_kicker, jiffies + HZ);
+}
+
+/******************** user/kernel context functions **********************/
+
+static int hil_mlc_serio_write(struct serio *serio, unsigned char c) {
+       struct hil_mlc_serio_map *map;
+       struct hil_mlc *mlc;
+       struct serio_driver *drv;
+       uint8_t *idx, *last;
+
+       map = serio->port_data;
+       if (map == NULL) {
+               BUG();
+               return -EIO;
+       }
+       mlc = map->mlc;
+       if (mlc == NULL) {
+               BUG();
+               return -EIO;
+       }
+       mlc->serio_opacket[map->didx] |= 
+               ((hil_packet)c) << (8 * (3 - mlc->serio_oidx[map->didx]));
+
+       if (mlc->serio_oidx[map->didx] >= 3) {
+               /* for now only commands */
+               if (!(mlc->serio_opacket[map->didx] & HIL_PKT_CMD)) 
+                       return -EIO;
+               switch (mlc->serio_opacket[map->didx] & HIL_PKT_DATA_MASK) {
+               case HIL_CMD_IDD:
+                       idx = mlc->di[map->didx].idd;
+                       goto emu;
+               case HIL_CMD_RSC:
+                       idx = mlc->di[map->didx].rsc;
+                       goto emu;
+               case HIL_CMD_EXD:
+                       idx = mlc->di[map->didx].exd;
+                       goto emu;
+               case HIL_CMD_RNM:
+                       idx = mlc->di[map->didx].rnm;
+                       goto emu;
+               default:
+                       break;
+               }
+               mlc->serio_oidx[map->didx] = 0;
+               mlc->serio_opacket[map->didx] = 0;
+       }
+
+       mlc->serio_oidx[map->didx]++;
+       return -EIO;
+ emu:
+       drv = serio->drv;
+       if (drv == NULL) {
+               BUG();
+               return -EIO;
+       }
+       last = idx + 15;
+       while ((last != idx) && (*last == 0)) last--;
+
+       while (idx != last) {
+               drv->interrupt(serio, 0, 0, NULL);
+               drv->interrupt(serio, HIL_ERR_INT >> 16, 0, NULL);
+               drv->interrupt(serio, 0, 0, NULL);
+               drv->interrupt(serio, *idx, 0, NULL);
+               idx++;
+       }
+       drv->interrupt(serio, 0, 0, NULL);
+       drv->interrupt(serio, HIL_ERR_INT >> 16, 0, NULL);
+       drv->interrupt(serio, HIL_PKT_CMD >> 8, 0, NULL);
+       drv->interrupt(serio, *idx, 0, NULL);
+       
+       mlc->serio_oidx[map->didx] = 0;
+       mlc->serio_opacket[map->didx] = 0;
+
+       return 0;
+}
+
+static int hil_mlc_serio_open(struct serio *serio) {
+       struct hil_mlc_serio_map *map;
+       struct hil_mlc *mlc;
+
+       if (serio->private != NULL) return -EBUSY;
+
+       map = serio->port_data;
+       if (map == NULL) {
+               BUG();
+               return -ENODEV;
+       }
+       mlc = map->mlc;
+       if (mlc == NULL) {
+               BUG();
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void hil_mlc_serio_close(struct serio *serio) {
+       struct hil_mlc_serio_map *map;
+       struct hil_mlc *mlc;
+
+       map = serio->port_data;
+       if (map == NULL) {
+               BUG();
+               return;
+       }
+       mlc = map->mlc;
+       if (mlc == NULL) {
+               BUG();
+               return;
+       }
+
+       serio->private = NULL;
+       serio->drv = NULL;
+       /* TODO wake up interruptable */
+}
+
+int hil_mlc_register(hil_mlc *mlc) {
+       int i;
+        unsigned long flags;
+
+       if (mlc == NULL) {
+               return -EINVAL;
+       }
+
+       mlc->istarted = 0;
+        mlc->ostarted = 0;
+
+        rwlock_init(&mlc->lock);
+        init_MUTEX(&(mlc->osem));
+
+        init_MUTEX(&(mlc->isem));
+        mlc->icount = -1;
+        mlc->imatch = 0;
+
+       mlc->opercnt = 0;
+
+        init_MUTEX_LOCKED(&(mlc->csem));
+
+       hil_mlc_clear_di_scratch(mlc);
+       hil_mlc_clear_di_map(mlc, 0);
+       for (i = 0; i < HIL_MLC_DEVMEM; i++) {
+               struct serio *mlc_serio;
+               hil_mlc_copy_di_scratch(mlc, i);
+               mlc_serio = kmalloc(sizeof(*mlc_serio), GFP_KERNEL);
+               mlc->serio[i] = mlc_serio;
+               memset(mlc_serio, 0, sizeof(*mlc_serio));
+               mlc_serio->type                 = SERIO_HIL | SERIO_HIL_MLC;
+               mlc_serio->write                = hil_mlc_serio_write;
+               mlc_serio->open                 = hil_mlc_serio_open;
+               mlc_serio->close                = hil_mlc_serio_close;
+               mlc_serio->port_data            = &(mlc->serio_map[i]);
+               mlc->serio_map[i].mlc           = mlc;
+               mlc->serio_map[i].didx          = i;
+               mlc->serio_map[i].di_revmap     = -1;
+               mlc->serio_opacket[i]           = 0;
+               mlc->serio_oidx[i]              = 0;
+               serio_register_port(mlc_serio);
+       }
+
+       mlc->tasklet = &hil_mlcs_tasklet;
+
+       write_lock_irqsave(&hil_mlcs_lock, flags);
+       list_add_tail(&mlc->list, &hil_mlcs);
+       mlc->seidx = HILSEN_START;
+       write_unlock_irqrestore(&hil_mlcs_lock, flags);
+
+       tasklet_schedule(&hil_mlcs_tasklet);
+       return 0;
+}
+
+int hil_mlc_unregister(hil_mlc *mlc) {
+       struct list_head *tmp;
+        unsigned long flags;
+       int i;
+
+       if (mlc == NULL)
+               return -EINVAL;
+
+       write_lock_irqsave(&hil_mlcs_lock, flags);
+       list_for_each(tmp, &hil_mlcs) {
+               if (list_entry(tmp, hil_mlc, list) == mlc)
+                       goto found;
+       }
+
+       /* not found in list */
+       write_unlock_irqrestore(&hil_mlcs_lock, flags);
+       tasklet_schedule(&hil_mlcs_tasklet);
+       return -ENODEV;
+
+ found:
+       list_del(tmp);
+        write_unlock_irqrestore(&hil_mlcs_lock, flags);
+
+       for (i = 0; i < HIL_MLC_DEVMEM; i++) {
+               serio_unregister_port(mlc->serio[i]);
+               mlc->serio[i] = NULL;
+       }
+
+       tasklet_schedule(&hil_mlcs_tasklet);
+       return 0;
+}
+
+/**************************** Module interface *************************/
+
+static int __init hil_mlc_init(void)
+{
+       init_timer(&hil_mlcs_kicker);
+       hil_mlcs_kicker.expires = jiffies + HZ;
+       hil_mlcs_kicker.function = &hil_mlcs_timer;
+       add_timer(&hil_mlcs_kicker);
+
+       tasklet_enable(&hil_mlcs_tasklet);
+
+       return 0;
+}
+                
+static void __exit hil_mlc_exit(void)
+{
+       del_timer(&hil_mlcs_kicker);
+
+       tasklet_disable(&hil_mlcs_tasklet);
+       tasklet_kill(&hil_mlcs_tasklet);
+}
+                        
+module_init(hil_mlc_init);
+module_exit(hil_mlc_exit);
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
new file mode 100644 (file)
index 0000000..7629452
--- /dev/null
@@ -0,0 +1,1054 @@
+/*
+ * HP i8042-based System Device Controller driver.
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("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
+ *
+ * References:
+ * System Device Controller Microprocessor Firmware Theory of Operation
+ *      for Part Number 1820-4784 Revision B.  Dwg No. A-1820-4784-2
+ * Helge Deller's original hilkbd.c port for PA-RISC.
+ *
+ *
+ * Driver theory of operation:
+ *
+ * hp_sdc_put does all writing to the SDC.  ISR can run on a different 
+ * CPU than hp_sdc_put, but only one CPU runs hp_sdc_put at a time 
+ * (it cannot really benefit from SMP anyway.)  A tasket fit this perfectly.
+ *
+ * All data coming back from the SDC is sent via interrupt and can be read 
+ * fully in the ISR, so there are no latency/throughput problems there.  
+ * The problem is with output, due to the slow clock speed of the SDC 
+ * compared to the CPU.  This should not be too horrible most of the time, 
+ * but if used with HIL devices that support the multibyte transfer command, 
+ * keeping outbound throughput flowing at the 6500KBps that the HIL is 
+ * capable of is more than can be done at HZ=100.
+ *
+ * Busy polling for IBF clear wastes CPU cycles and bus cycles.  hp_sdc.ibf 
+ * is set to 0 when the IBF flag in the status register has cleared.  ISR 
+ * may do this, and may also access the parts of queued transactions related 
+ * to reading data back from the SDC, but otherwise will not touch the 
+ * hp_sdc state. Whenever a register is written hp_sdc.ibf is set to 1.
+ *
+ * The i8042 write index and the values in the 4-byte input buffer
+ * starting at 0x70 are kept track of in hp_sdc.wi, and .r7[], respectively,
+ * to minimize the amount of IO needed to the SDC.  However these values 
+ * do not need to be locked since they are only ever accessed by hp_sdc_put.
+ *
+ * A timer task schedules the tasklet once per second just to make
+ * sure it doesn't freeze up and to allow for bad reads to time out.
+ */
+
+#include <linux/hp_sdc.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+#include <linux/hil.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+/* Machine-specific abstraction */
+
+#if defined(__hppa__)
+# include <asm/parisc-device.h>
+# define sdc_readb(p)          gsc_readb(p)
+# define sdc_writeb(v,p)       gsc_writeb((v),(p))
+#elif defined(__mc68000__)
+# include <asm/uaccess.h>
+# define sdc_readb(p)          in_8(p)
+# define sdc_writeb(v,p)       out_8((p),(v))
+#else
+# error "HIL is not supported on this platform"
+#endif
+
+#define PREFIX "HP SDC: "
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("HP i8042-based SDC Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+EXPORT_SYMBOL(hp_sdc_request_timer_irq);
+EXPORT_SYMBOL(hp_sdc_request_hil_irq);
+EXPORT_SYMBOL(hp_sdc_request_cooked_irq);
+
+EXPORT_SYMBOL(hp_sdc_release_timer_irq);
+EXPORT_SYMBOL(hp_sdc_release_hil_irq);
+EXPORT_SYMBOL(hp_sdc_release_cooked_irq);
+
+EXPORT_SYMBOL(hp_sdc_enqueue_transaction);
+EXPORT_SYMBOL(hp_sdc_dequeue_transaction);
+
+static hp_i8042_sdc    hp_sdc; /* All driver state is kept in here. */
+
+/*************** primitives for use in any context *********************/
+static inline uint8_t hp_sdc_status_in8 (void) {
+       uint8_t status;
+       unsigned long flags;
+
+       write_lock_irqsave(&hp_sdc.ibf_lock, flags);
+       status = sdc_readb(hp_sdc.status_io);
+       if (!(status & HP_SDC_STATUS_IBF)) hp_sdc.ibf = 0;
+       write_unlock_irqrestore(&hp_sdc.ibf_lock, flags);
+
+       return status;
+}
+
+static inline uint8_t hp_sdc_data_in8 (void) {
+       return sdc_readb(hp_sdc.data_io); 
+}
+
+static inline void hp_sdc_status_out8 (uint8_t val) {
+       unsigned long flags;
+
+       write_lock_irqsave(&hp_sdc.ibf_lock, flags);
+       hp_sdc.ibf = 1;
+       if ((val & 0xf0) == 0xe0) hp_sdc.wi = 0xff;
+       sdc_writeb(val, hp_sdc.status_io);
+       write_unlock_irqrestore(&hp_sdc.ibf_lock, flags);
+}
+
+static inline void hp_sdc_data_out8 (uint8_t val) {
+       unsigned long flags;
+
+       write_lock_irqsave(&hp_sdc.ibf_lock, flags);
+       hp_sdc.ibf = 1;
+       sdc_writeb(val, hp_sdc.data_io);
+       write_unlock_irqrestore(&hp_sdc.ibf_lock, flags);
+}
+
+/*     Care must be taken to only invoke hp_sdc_spin_ibf when 
+ *     absolutely needed, or in rarely invoked subroutines.  
+ *     Not only does it waste CPU cycles, it also wastes bus cycles. 
+ */
+static inline void hp_sdc_spin_ibf(void) {
+       unsigned long flags;
+       rwlock_t *lock;
+
+       lock = &hp_sdc.ibf_lock;
+
+       read_lock_irqsave(lock, flags);
+       if (!hp_sdc.ibf) {
+               read_unlock_irqrestore(lock, flags);
+               return;
+       }
+       read_unlock(lock);
+       write_lock(lock);
+       while (sdc_readb(hp_sdc.status_io) & HP_SDC_STATUS_IBF) {};
+       hp_sdc.ibf = 0;
+       write_unlock_irqrestore(lock, flags);
+}
+
+
+/************************ Interrupt context functions ************************/
+static void hp_sdc_take (int irq, void *dev_id, uint8_t status, uint8_t data) {
+       hp_sdc_transaction *curr;
+
+       read_lock(&hp_sdc.rtq_lock);
+       if (hp_sdc.rcurr < 0) {
+               read_unlock(&hp_sdc.rtq_lock);
+               return;
+       }
+       curr = hp_sdc.tq[hp_sdc.rcurr];
+       read_unlock(&hp_sdc.rtq_lock);
+
+       curr->seq[curr->idx++] = status;
+       curr->seq[curr->idx++] = data;
+       hp_sdc.rqty -= 2;
+       do_gettimeofday(&hp_sdc.rtv);
+
+       if (hp_sdc.rqty <= 0) {
+               /* All data has been gathered. */
+               if(curr->seq[curr->actidx] & HP_SDC_ACT_SEMAPHORE) {
+                       if (curr->act.semaphore) up(curr->act.semaphore);
+               }
+               if(curr->seq[curr->actidx] & HP_SDC_ACT_CALLBACK) {
+                       if (curr->act.irqhook)
+                               curr->act.irqhook(irq, dev_id, status, data);
+               }
+               curr->actidx = curr->idx;
+               curr->idx++;
+               /* Return control of this transaction */
+               write_lock(&hp_sdc.rtq_lock);
+               hp_sdc.rcurr = -1; 
+               hp_sdc.rqty = 0;
+               write_unlock(&hp_sdc.rtq_lock);
+               tasklet_schedule(&hp_sdc.task);
+       }
+}
+
+static irqreturn_t hp_sdc_isr(int irq, void *dev_id, struct pt_regs * regs) {
+       uint8_t status, data;
+
+       status = hp_sdc_status_in8();
+       /* Read data unconditionally to advance i8042. */
+       data =   hp_sdc_data_in8();
+
+       /* For now we are ignoring these until we get the SDC to behave. */
+       if (((status & 0xf1) == 0x51) && data == 0x82) {
+         return IRQ_HANDLED;
+       }
+
+       switch(status & HP_SDC_STATUS_IRQMASK) {
+             case 0: /* This case is not documented. */
+               break;
+             case HP_SDC_STATUS_USERTIMER:
+             case HP_SDC_STATUS_PERIODIC:
+             case HP_SDC_STATUS_TIMER:
+               read_lock(&hp_sdc.hook_lock);
+               if (hp_sdc.timer != NULL)
+                       hp_sdc.timer(irq, dev_id, status, data);
+               read_unlock(&hp_sdc.hook_lock);
+               break;
+             case HP_SDC_STATUS_REG:
+               hp_sdc_take(irq, dev_id, status, data);
+               break;
+             case HP_SDC_STATUS_HILCMD:
+             case HP_SDC_STATUS_HILDATA:
+               read_lock(&hp_sdc.hook_lock);
+               if (hp_sdc.hil != NULL)
+                       hp_sdc.hil(irq, dev_id, status, data);
+               read_unlock(&hp_sdc.hook_lock);
+               break;
+             case HP_SDC_STATUS_PUP:
+               read_lock(&hp_sdc.hook_lock);
+               if (hp_sdc.pup != NULL)
+                       hp_sdc.pup(irq, dev_id, status, data);
+               else printk(KERN_INFO PREFIX "HP SDC reports successful PUP.\n");
+               read_unlock(&hp_sdc.hook_lock);
+               break;
+             default:
+               read_lock(&hp_sdc.hook_lock);
+               if (hp_sdc.cooked != NULL)
+                       hp_sdc.cooked(irq, dev_id, status, data);
+               read_unlock(&hp_sdc.hook_lock);
+               break;
+       }
+       return IRQ_HANDLED;
+}
+
+
+static irqreturn_t hp_sdc_nmisr(int irq, void *dev_id, struct pt_regs * regs) {
+       int status;
+       
+       status = hp_sdc_status_in8();
+       printk(KERN_WARNING PREFIX "NMI !\n");
+
+#if 0  
+       if (status & HP_SDC_NMISTATUS_FHS) {
+               read_lock(&hp_sdc.hook_lock);
+               if (hp_sdc.timer != NULL)
+                       hp_sdc.timer(irq, dev_id, status, 0);
+               read_unlock(&hp_sdc.hook_lock);
+       }
+       else {
+               /* TODO: pass this on to the HIL handler, or do SAK here? */
+               printk(KERN_WARNING PREFIX "HIL NMI\n");
+       }
+#endif
+       return IRQ_HANDLED;
+}
+
+
+/***************** Kernel (tasklet) context functions ****************/
+
+unsigned long hp_sdc_put(void);
+
+static void hp_sdc_tasklet(unsigned long foo) {
+
+       write_lock_irq(&hp_sdc.rtq_lock);
+       if (hp_sdc.rcurr >= 0) {
+               struct timeval tv;
+               do_gettimeofday(&tv);
+               if (tv.tv_sec > hp_sdc.rtv.tv_sec) tv.tv_usec += 1000000;
+               if (tv.tv_usec - hp_sdc.rtv.tv_usec > HP_SDC_MAX_REG_DELAY) {
+                       hp_sdc_transaction *curr;
+                       uint8_t tmp;
+
+                       curr = hp_sdc.tq[hp_sdc.rcurr];
+                       /* If this turns out to be a normal failure mode
+                        * we'll need to figure out a way to communicate
+                        * it back to the application. and be less verbose.
+                        */
+                       printk(KERN_WARNING PREFIX "read timeout (%ius)!\n",
+                              tv.tv_usec - hp_sdc.rtv.tv_usec);
+                       curr->idx += hp_sdc.rqty;
+                       hp_sdc.rqty = 0;
+                       tmp = curr->seq[curr->actidx];
+                       curr->seq[curr->actidx] |= HP_SDC_ACT_DEAD;
+                       if(tmp & HP_SDC_ACT_SEMAPHORE) {
+                               if (curr->act.semaphore) 
+                                       up(curr->act.semaphore);
+                       }
+                       if(tmp & HP_SDC_ACT_CALLBACK) {
+                               /* Note this means that irqhooks may be called
+                                * in tasklet/bh context.
+                                */
+                               if (curr->act.irqhook) 
+                                       curr->act.irqhook(0, 0, 0, 0);
+                       }
+                       curr->actidx = curr->idx;
+                       curr->idx++;
+                       hp_sdc.rcurr = -1; 
+               }
+       }
+       write_unlock_irq(&hp_sdc.rtq_lock);
+       hp_sdc_put();
+}
+
+unsigned long hp_sdc_put(void) {
+       hp_sdc_transaction *curr;
+       uint8_t act;
+       int idx, curridx;
+
+       int limit = 0;
+
+       write_lock(&hp_sdc.lock);
+
+       /* If i8042 buffers are full, we cannot do anything that
+          requires output, so we skip to the administrativa. */
+       if (hp_sdc.ibf) {
+               hp_sdc_status_in8();
+               if (hp_sdc.ibf) goto finish;
+       }
+
+ anew:
+       /* See if we are in the middle of a sequence. */
+       if (hp_sdc.wcurr < 0) hp_sdc.wcurr = 0;
+       read_lock_irq(&hp_sdc.rtq_lock);
+       if (hp_sdc.rcurr == hp_sdc.wcurr) hp_sdc.wcurr++;
+       read_unlock_irq(&hp_sdc.rtq_lock);
+       if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0;
+       curridx = hp_sdc.wcurr;
+
+       if (hp_sdc.tq[curridx] != NULL) goto start;
+
+       while (++curridx != hp_sdc.wcurr) {
+               if (curridx >= HP_SDC_QUEUE_LEN) {
+                       curridx = -1; /* Wrap to top */
+                       continue;
+               }
+               read_lock_irq(&hp_sdc.rtq_lock);
+               if (hp_sdc.rcurr == curridx) {
+                       read_unlock_irq(&hp_sdc.rtq_lock);
+                       continue;
+               }
+               read_unlock_irq(&hp_sdc.rtq_lock);
+               if (hp_sdc.tq[curridx] != NULL) break; /* Found one. */
+       }
+       if (curridx == hp_sdc.wcurr) { /* There's nothing queued to do. */
+               curridx = -1;
+       }
+       hp_sdc.wcurr = curridx;
+
+ start:
+
+       /* Check to see if the interrupt mask needs to be set. */
+       if (hp_sdc.set_im) {
+               hp_sdc_status_out8(hp_sdc.im | HP_SDC_CMD_SET_IM);
+               hp_sdc.set_im = 0;
+               goto finish;
+       }
+
+       if (hp_sdc.wcurr == -1) goto done;
+
+       curr = hp_sdc.tq[curridx];
+       idx = curr->actidx;
+
+       if (curr->actidx >= curr->endidx) {
+               hp_sdc.tq[curridx] = NULL;
+               /* Interleave outbound data between the transactions. */
+               hp_sdc.wcurr++;
+               if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0;
+               goto finish;    
+       }
+
+       act = curr->seq[idx];
+       idx++;
+
+       if (curr->idx >= curr->endidx) {
+               if (act & HP_SDC_ACT_DEALLOC) kfree(curr);
+               hp_sdc.tq[curridx] = NULL;
+               /* Interleave outbound data between the transactions. */
+               hp_sdc.wcurr++;
+               if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0;
+               goto finish;    
+       }
+
+       while (act & HP_SDC_ACT_PRECMD) {
+               if (curr->idx != idx) {
+                       idx++;
+                       act &= ~HP_SDC_ACT_PRECMD;
+                       break;
+               }
+               hp_sdc_status_out8(curr->seq[idx]);
+               curr->idx++;
+               /* act finished? */
+               if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_PRECMD)
+                 goto actdone;
+               /* skip quantity field if data-out sequence follows. */
+               if (act & HP_SDC_ACT_DATAOUT) curr->idx++;
+               goto finish;
+       }
+       if (act & HP_SDC_ACT_DATAOUT) {
+               int qty;
+
+               qty = curr->seq[idx];
+               idx++;
+               if (curr->idx - idx < qty) {
+                       hp_sdc_data_out8(curr->seq[curr->idx]);
+                       curr->idx++;
+                       /* act finished? */
+                       if ((curr->idx - idx >= qty) && 
+                           ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAOUT))
+                               goto actdone;
+                       goto finish;
+               }
+               idx += qty;
+               act &= ~HP_SDC_ACT_DATAOUT;
+       }
+       else while (act & HP_SDC_ACT_DATAREG) {
+               int mask;
+               uint8_t w7[4];
+
+               mask = curr->seq[idx];
+               if (idx != curr->idx) {
+                       idx++;
+                       idx += !!(mask & 1);
+                       idx += !!(mask & 2);
+                       idx += !!(mask & 4);
+                       idx += !!(mask & 8);
+                       act &= ~HP_SDC_ACT_DATAREG;
+                       break;
+               }
+               
+               w7[0] = (mask & 1) ? curr->seq[++idx] : hp_sdc.r7[0];
+               w7[1] = (mask & 2) ? curr->seq[++idx] : hp_sdc.r7[1];
+               w7[2] = (mask & 4) ? curr->seq[++idx] : hp_sdc.r7[2];
+               w7[3] = (mask & 8) ? curr->seq[++idx] : hp_sdc.r7[3];
+               
+               if (hp_sdc.wi > 0x73 || hp_sdc.wi < 0x70 ||
+                       w7[hp_sdc.wi-0x70] == hp_sdc.r7[hp_sdc.wi-0x70]) {
+                       int i = 0;
+
+                       /* Need to point the write index register */    
+                       while ((i < 4) && w7[i] == hp_sdc.r7[i]) i++;
+                       if (i < 4) {
+                               hp_sdc_status_out8(HP_SDC_CMD_SET_D0 + i);
+                               hp_sdc.wi = 0x70 + i;
+                               goto finish;
+                       }
+                       idx++;
+                       if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAREG)
+                               goto actdone;
+                       curr->idx = idx;
+                       act &= ~HP_SDC_ACT_DATAREG;
+                       break;
+               }
+
+               hp_sdc_data_out8(w7[hp_sdc.wi - 0x70]);
+               hp_sdc.r7[hp_sdc.wi - 0x70] = w7[hp_sdc.wi - 0x70];
+               hp_sdc.wi++; /* write index register autoincrements */
+               {
+                       int i = 0;
+
+                       while ((i < 4) && w7[i] == hp_sdc.r7[i]) i++;
+                       if (i >= 4) {
+                               curr->idx = idx + 1;
+                               if ((act & HP_SDC_ACT_DURING) == 
+                                   HP_SDC_ACT_DATAREG)
+                                       goto actdone;
+                       }
+               }
+               goto finish;
+       }
+       /* We don't go any further in the command if there is a pending read,
+          because we don't want interleaved results. */
+       read_lock_irq(&hp_sdc.rtq_lock);
+       if (hp_sdc.rcurr >= 0) {
+               read_unlock_irq(&hp_sdc.rtq_lock);
+               goto finish;
+       }
+       read_unlock_irq(&hp_sdc.rtq_lock);
+
+
+       if (act & HP_SDC_ACT_POSTCMD) {
+               uint8_t postcmd;
+
+               /* curr->idx should == idx at this point. */
+               postcmd = curr->seq[idx];
+               curr->idx++;
+               if (act & HP_SDC_ACT_DATAIN) {
+
+                       /* Start a new read */
+                       hp_sdc.rqty = curr->seq[curr->idx];
+                       do_gettimeofday(&hp_sdc.rtv);
+                       curr->idx++;
+                       /* Still need to lock here in case of spurious irq. */
+                       write_lock_irq(&hp_sdc.rtq_lock);
+                       hp_sdc.rcurr = curridx; 
+                       write_unlock_irq(&hp_sdc.rtq_lock);
+                       hp_sdc_status_out8(postcmd);
+                       goto finish;
+               }
+               hp_sdc_status_out8(postcmd);
+               goto actdone;
+       }
+
+actdone:
+       if (act & HP_SDC_ACT_SEMAPHORE) {
+               up(curr->act.semaphore);
+       }
+       else if (act & HP_SDC_ACT_CALLBACK) {
+               curr->act.irqhook(0,0,0,0);
+       }
+       if (curr->idx >= curr->endidx) { /* This transaction is over. */
+               if (act & HP_SDC_ACT_DEALLOC) kfree(curr);
+               hp_sdc.tq[curridx] = NULL;
+       }
+       else {
+               curr->actidx = idx + 1;
+               curr->idx = idx + 2;
+       }
+       /* Interleave outbound data between the transactions. */
+       hp_sdc.wcurr++;
+       if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) hp_sdc.wcurr = 0;
+
+ finish:
+       /* If by some quirk IBF has cleared and our ISR has run to 
+          see that that has happened, do it all again. */
+       if (!hp_sdc.ibf && limit++ < 20) goto anew;
+
+ done:
+       if (hp_sdc.wcurr >= 0) tasklet_schedule(&hp_sdc.task);
+       write_unlock(&hp_sdc.lock);
+       return 0;
+}
+
+/******* Functions called in either user or kernel context ****/
+int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) {
+       unsigned long flags;
+       int i;
+
+       if (this == NULL) {
+               tasklet_schedule(&hp_sdc.task);
+               return -EINVAL;
+       };
+
+       write_lock_irqsave(&hp_sdc.lock, flags);
+
+       /* Can't have same transaction on queue twice */
+       for (i=0; i < HP_SDC_QUEUE_LEN; i++)
+               if (hp_sdc.tq[i] == this) goto fail;
+
+       this->actidx = 0;
+       this->idx = 1;
+
+       /* Search for empty slot */
+       for (i=0; i < HP_SDC_QUEUE_LEN; i++) {
+               if (hp_sdc.tq[i] == NULL) {
+                       hp_sdc.tq[i] = this;
+                       write_unlock_irqrestore(&hp_sdc.lock, flags);
+                       tasklet_schedule(&hp_sdc.task);
+                       return 0;
+               }
+       }
+       write_unlock_irqrestore(&hp_sdc.lock, flags);
+       printk(KERN_WARNING PREFIX "No free slot to add transaction.\n");
+       return -EBUSY;
+
+ fail:
+       write_unlock_irqrestore(&hp_sdc.lock,flags);
+       printk(KERN_WARNING PREFIX "Transaction add failed: transaction already queued?\n");
+       return -EINVAL;
+}
+
+int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) {
+       unsigned long flags;
+       int i;
+
+       write_lock_irqsave(&hp_sdc.lock, flags);
+
+       /* TODO: don't remove it if it's not done. */
+
+       for (i=0; i < HP_SDC_QUEUE_LEN; i++)
+               if (hp_sdc.tq[i] == this) hp_sdc.tq[i] = NULL;
+
+       write_unlock_irqrestore(&hp_sdc.lock, flags);
+       return 0;
+}
+
+
+
+/********************** User context functions **************************/
+int hp_sdc_request_timer_irq(hp_sdc_irqhook *callback) {
+
+       if (callback == NULL || hp_sdc.dev == NULL) {
+               return -EINVAL;
+       }
+       write_lock_irq(&hp_sdc.hook_lock);
+       if (hp_sdc.timer != NULL) {
+               write_unlock_irq(&hp_sdc.hook_lock);
+               return -EBUSY;
+       }
+
+       hp_sdc.timer = callback;
+       /* Enable interrupts from the timers */
+       hp_sdc.im &= ~HP_SDC_IM_FH;
+        hp_sdc.im &= ~HP_SDC_IM_PT;
+       hp_sdc.im &= ~HP_SDC_IM_TIMERS;
+       hp_sdc.set_im = 1;
+       write_unlock_irq(&hp_sdc.hook_lock);
+
+       tasklet_schedule(&hp_sdc.task);
+
+       return 0;
+}
+
+int hp_sdc_request_hil_irq(hp_sdc_irqhook *callback) {
+
+       if (callback == NULL || hp_sdc.dev == NULL) {
+               return -EINVAL;
+       }
+       write_lock_irq(&hp_sdc.hook_lock);
+       if (hp_sdc.hil != NULL) {
+               write_unlock_irq(&hp_sdc.hook_lock);
+               return -EBUSY;
+       }
+
+       hp_sdc.hil = callback;
+       hp_sdc.im &= ~(HP_SDC_IM_HIL | HP_SDC_IM_RESET);
+       hp_sdc.set_im = 1;
+       write_unlock_irq(&hp_sdc.hook_lock);
+
+       tasklet_schedule(&hp_sdc.task);
+
+       return 0;
+}
+
+int hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback) {
+
+       if (callback == NULL || hp_sdc.dev == NULL) {
+               return -EINVAL;
+       }
+       write_lock_irq(&hp_sdc.hook_lock);
+       if (hp_sdc.cooked != NULL) {
+               write_unlock_irq(&hp_sdc.hook_lock);
+               return -EBUSY;
+       }
+
+       /* Enable interrupts from the HIL MLC */
+       hp_sdc.cooked = callback;
+       hp_sdc.im &= ~(HP_SDC_IM_HIL | HP_SDC_IM_RESET);
+       hp_sdc.set_im = 1;
+       write_unlock_irq(&hp_sdc.hook_lock);
+
+       tasklet_schedule(&hp_sdc.task);
+
+       return 0;
+}
+
+int hp_sdc_release_timer_irq(hp_sdc_irqhook *callback) {
+
+
+       write_lock_irq(&hp_sdc.hook_lock);
+       if ((callback != hp_sdc.timer) ||
+           (hp_sdc.timer == NULL)) {
+               write_unlock_irq(&hp_sdc.hook_lock);
+               return -EINVAL;
+       }
+
+       /* Disable interrupts from the timers */
+       hp_sdc.timer = NULL;
+       hp_sdc.im |= HP_SDC_IM_TIMERS;
+       hp_sdc.im |= HP_SDC_IM_FH;
+       hp_sdc.im |= HP_SDC_IM_PT;
+       hp_sdc.set_im = 1;
+       write_unlock_irq(&hp_sdc.hook_lock);
+       tasklet_schedule(&hp_sdc.task);
+
+       return 0;
+}
+
+int hp_sdc_release_hil_irq(hp_sdc_irqhook *callback) {
+
+       write_lock_irq(&hp_sdc.hook_lock);
+       if ((callback != hp_sdc.hil) ||
+           (hp_sdc.hil == NULL)) {
+               write_unlock_irq(&hp_sdc.hook_lock);
+               return -EINVAL;
+       }
+
+       hp_sdc.hil = NULL;
+       /* Disable interrupts from HIL only if there is no cooked driver. */
+       if(hp_sdc.cooked == NULL) {
+               hp_sdc.im |= (HP_SDC_IM_HIL | HP_SDC_IM_RESET);
+               hp_sdc.set_im = 1;
+       }
+       write_unlock_irq(&hp_sdc.hook_lock);
+       tasklet_schedule(&hp_sdc.task);
+
+       return 0;
+}
+
+int hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback) {
+
+       write_lock_irq(&hp_sdc.hook_lock);
+       if ((callback != hp_sdc.cooked) ||
+           (hp_sdc.cooked == NULL)) {
+               write_unlock_irq(&hp_sdc.hook_lock);
+               return -EINVAL;
+       }
+
+       hp_sdc.cooked = NULL;
+       /* Disable interrupts from HIL only if there is no raw HIL driver. */
+       if(hp_sdc.hil == NULL) {
+               hp_sdc.im |= (HP_SDC_IM_HIL | HP_SDC_IM_RESET);
+               hp_sdc.set_im = 1;
+       }
+       write_unlock_irq(&hp_sdc.hook_lock);
+       tasklet_schedule(&hp_sdc.task);
+
+       return 0;
+}
+
+/************************* Keepalive timer task *********************/
+
+void hp_sdc_kicker (unsigned long data) {
+       tasklet_schedule(&hp_sdc.task);
+       /* Re-insert the periodic task. */
+       mod_timer(&hp_sdc.kicker, jiffies + HZ);
+}
+
+/************************** Module Initialization ***************************/
+
+#if defined(__hppa__)
+
+static struct parisc_device_id hp_sdc_tbl[] = {
+       {
+               .hw_type =      HPHW_FIO, 
+               .hversion_rev = HVERSION_REV_ANY_ID,
+               .hversion =     HVERSION_ANY_ID,
+               .sversion =     0x73, 
+        },
+       { 0, }
+};
+
+MODULE_DEVICE_TABLE(parisc, hp_sdc_tbl);
+
+static int __init hp_sdc_init_hppa(struct parisc_device *d);
+
+static struct parisc_driver hp_sdc_driver = {
+       .name =         "HP SDC",
+       .id_table =     hp_sdc_tbl,
+       .probe =        hp_sdc_init_hppa,
+};
+
+#endif /* __hppa__ */
+
+static int __init hp_sdc_init(void)
+{
+       int i;
+       char *errstr;
+       hp_sdc_transaction t_sync;
+       uint8_t ts_sync[6];
+       struct semaphore s_sync;
+
+       rwlock_init(&hp_sdc.lock);
+       rwlock_init(&hp_sdc.ibf_lock);
+       rwlock_init(&hp_sdc.rtq_lock);
+       rwlock_init(&hp_sdc.hook_lock);
+
+       hp_sdc.timer            = NULL;
+       hp_sdc.hil              = NULL;
+       hp_sdc.pup              = NULL;
+       hp_sdc.cooked           = NULL;
+       hp_sdc.im               = HP_SDC_IM_MASK;  /* Mask maskable irqs */
+       hp_sdc.set_im           = 1;
+       hp_sdc.wi               = 0xff;
+       hp_sdc.r7[0]            = 0xff;
+       hp_sdc.r7[1]            = 0xff;
+       hp_sdc.r7[2]            = 0xff;
+       hp_sdc.r7[3]            = 0xff;
+       hp_sdc.ibf              = 1;
+
+       for (i = 0; i < HP_SDC_QUEUE_LEN; i++) hp_sdc.tq[i] = NULL;
+       hp_sdc.wcurr            = -1;
+        hp_sdc.rcurr           = -1;
+       hp_sdc.rqty             = 0;
+
+       hp_sdc.dev_err = -ENODEV;
+
+       errstr = "IO not found for";
+       if (!hp_sdc.base_io) goto err0;
+
+       errstr = "IRQ not found for";
+       if (!hp_sdc.irq) goto err0;
+
+       hp_sdc.dev_err = -EBUSY;
+
+#if defined(__hppa__)
+       errstr = "IO not available for";
+        if (request_region(hp_sdc.data_io, 2, hp_sdc_driver.name)) goto err0;
+#endif 
+
+       errstr = "IRQ not available for";
+        if(request_irq(hp_sdc.irq, &hp_sdc_isr, 0, "HP SDC",
+                      (void *) hp_sdc.base_io)) goto err1;
+
+       errstr = "NMI not available for";
+       if (request_irq(hp_sdc.nmi, &hp_sdc_nmisr, 0, "HP SDC NMI", 
+                       (void *) hp_sdc.base_io)) goto err2;
+
+       printk(KERN_INFO PREFIX "HP SDC at 0x%p, IRQ %d (NMI IRQ %d)\n", 
+              (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi);
+
+       hp_sdc_status_in8();
+       hp_sdc_data_in8();
+
+       tasklet_init(&hp_sdc.task, hp_sdc_tasklet, 0);
+
+       /* Sync the output buffer registers, thus scheduling hp_sdc_tasklet. */
+       t_sync.actidx   = 0;
+       t_sync.idx      = 1;
+       t_sync.endidx   = 6;
+       t_sync.seq      = ts_sync;
+       ts_sync[0]      = HP_SDC_ACT_DATAREG | HP_SDC_ACT_SEMAPHORE;
+       ts_sync[1]      = 0x0f;
+       ts_sync[2] = ts_sync[3] = ts_sync[4] = ts_sync[5] = 0;
+       t_sync.act.semaphore = &s_sync;
+       init_MUTEX_LOCKED(&s_sync);
+       hp_sdc_enqueue_transaction(&t_sync);
+       down(&s_sync); /* Wait for t_sync to complete */
+
+       /* Create the keepalive task */
+       init_timer(&hp_sdc.kicker);
+       hp_sdc.kicker.expires = jiffies + HZ;
+       hp_sdc.kicker.function = &hp_sdc_kicker;
+       add_timer(&hp_sdc.kicker);
+
+       hp_sdc.dev_err = 0;
+       return 0;
+ err2:
+       free_irq(hp_sdc.irq, NULL);
+ err1:
+       release_region(hp_sdc.data_io, 2);
+ err0:
+       printk(KERN_WARNING PREFIX ": %s SDC IO=0x%p IRQ=0x%x NMI=0x%x\n", 
+               errstr, (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi);
+       hp_sdc.dev = NULL;
+       return hp_sdc.dev_err;
+}
+
+#if defined(__hppa__)
+
+static int __init hp_sdc_init_hppa(struct parisc_device *d)
+{
+       if (!d) return 1;
+       if (hp_sdc.dev != NULL) return 1;       /* We only expect one SDC */
+
+       hp_sdc.dev              = d;
+       hp_sdc.irq              = d->irq;
+       hp_sdc.nmi              = d->aux_irq;
+       hp_sdc.base_io          = d->hpa;
+       hp_sdc.data_io          = d->hpa + 0x800;
+       hp_sdc.status_io        = d->hpa + 0x801;
+
+       return hp_sdc_init();
+}
+
+#endif /* __hppa__ */
+
+#if !defined(__mc68000__) /* Link error on m68k! */
+static void __exit hp_sdc_exit(void)
+#else
+static void hp_sdc_exit(void)
+#endif
+{
+       write_lock_irq(&hp_sdc.lock);
+
+       /* Turn off all maskable "sub-function" irq's. */
+       hp_sdc_spin_ibf();
+       sdc_writeb(HP_SDC_CMD_SET_IM | HP_SDC_IM_MASK, hp_sdc.status_io);
+
+       /* Wait until we know this has been processed by the i8042 */
+       hp_sdc_spin_ibf();
+
+       free_irq(hp_sdc.nmi, NULL);
+       free_irq(hp_sdc.irq, NULL);
+       write_unlock_irq(&hp_sdc.lock);
+
+       del_timer(&hp_sdc.kicker);
+
+       tasklet_kill(&hp_sdc.task);
+
+/*        release_region(hp_sdc.data_io, 2); */
+
+#if defined(__hppa__)
+       if (unregister_parisc_driver(&hp_sdc_driver)) 
+               printk(KERN_WARNING PREFIX "Error unregistering HP SDC");
+#endif
+}
+
+static int __init hp_sdc_register(void)
+{
+       hp_sdc_transaction tq_init;
+       uint8_t tq_init_seq[5];
+       struct semaphore tq_init_sem;
+#if defined(__mc68000__)
+       mm_segment_t fs;
+       unsigned char i;
+#endif
+       
+       hp_sdc.dev = NULL;
+       hp_sdc.dev_err = 0;
+#if defined(__hppa__)
+       if (register_parisc_driver(&hp_sdc_driver)) {
+               printk(KERN_WARNING PREFIX "Error registering SDC with system bus tree.\n");
+               return -ENODEV;
+       }
+#elif defined(__mc68000__)
+       if (!MACH_IS_HP300)
+           return -ENODEV;
+
+       hp_sdc.irq       = 1;
+       hp_sdc.nmi       = 7;
+       hp_sdc.base_io   = (unsigned long) 0xf0428000;
+       hp_sdc.data_io   = (unsigned long) hp_sdc.base_io + 1;
+       hp_sdc.status_io = (unsigned long) hp_sdc.base_io + 3;
+       fs = get_fs();
+       set_fs(KERNEL_DS);
+       if (!get_user(i, (unsigned char *)hp_sdc.data_io))
+               hp_sdc.dev = (void *)1;
+       set_fs(fs);
+       hp_sdc.dev_err   = hp_sdc_init();
+#endif
+       if (hp_sdc.dev == NULL) {
+               printk(KERN_WARNING PREFIX "No SDC found.\n");
+               return hp_sdc.dev_err;
+       }
+
+       init_MUTEX_LOCKED(&tq_init_sem);
+
+       tq_init.actidx          = 0;
+       tq_init.idx             = 1;
+       tq_init.endidx          = 5;
+       tq_init.seq             = tq_init_seq;
+       tq_init.act.semaphore   = &tq_init_sem;
+
+       tq_init_seq[0] = 
+         HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE;
+       tq_init_seq[1] = HP_SDC_CMD_READ_KCC;
+       tq_init_seq[2] = 1;
+       tq_init_seq[3] = 0;
+       tq_init_seq[4] = 0;
+
+       hp_sdc_enqueue_transaction(&tq_init);
+
+       down(&tq_init_sem);
+       up(&tq_init_sem);
+
+       if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) {
+               printk(KERN_WARNING PREFIX "Error reading config byte.\n");
+               hp_sdc_exit();
+               return -ENODEV;
+       }
+       hp_sdc.r11 = tq_init_seq[4];
+       if (hp_sdc.r11 & HP_SDC_CFG_NEW) {
+               char *str;
+               printk(KERN_INFO PREFIX "New style SDC\n");
+               tq_init_seq[1] = HP_SDC_CMD_READ_XTD;
+               tq_init.actidx          = 0;
+               tq_init.idx             = 1;
+               down(&tq_init_sem);
+               hp_sdc_enqueue_transaction(&tq_init);           
+               down(&tq_init_sem);
+               up(&tq_init_sem);
+               if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) {
+                       printk(KERN_WARNING PREFIX "Error reading extended config byte.\n");
+                       return -ENODEV;
+               }
+               hp_sdc.r7e = tq_init_seq[4];
+               HP_SDC_XTD_REV_STRINGS(hp_sdc.r7e & HP_SDC_XTD_REV, str)
+               printk(KERN_INFO PREFIX "Revision: %s\n", str);
+               if (hp_sdc.r7e & HP_SDC_XTD_BEEPER) {
+                       printk(KERN_INFO PREFIX "TI SN76494 beeper present\n");
+               }
+               if (hp_sdc.r7e & HP_SDC_XTD_BBRTC) {
+                       printk(KERN_INFO PREFIX "OKI MSM-58321 BBRTC present\n");
+               }
+               printk(KERN_INFO PREFIX "Spunking the self test register to force PUP "
+                      "on next firmware reset.\n");
+               tq_init_seq[0] = HP_SDC_ACT_PRECMD | 
+                       HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE;
+               tq_init_seq[1] = HP_SDC_CMD_SET_STR;
+               tq_init_seq[2] = 1;
+               tq_init_seq[3] = 0;
+               tq_init.actidx          = 0;
+               tq_init.idx             = 1;
+               tq_init.endidx          = 4;
+               down(&tq_init_sem);
+               hp_sdc_enqueue_transaction(&tq_init);           
+               down(&tq_init_sem);
+               up(&tq_init_sem);
+       }
+       else {
+               printk(KERN_INFO PREFIX "Old style SDC (1820-%s).\n", 
+                      (hp_sdc.r11 & HP_SDC_CFG_REV) ? "3300" : "2564/3087");
+       }
+
+        return 0;
+}
+
+module_init(hp_sdc_register);
+module_exit(hp_sdc_exit);
+
+/* Timing notes:  These measurements taken on my 64MHz 7100-LC (715/64) 
+ *                                              cycles cycles-adj    time
+ * between two consecutive mfctl(16)'s:              4        n/a    63ns
+ * hp_sdc_spin_ibf when idle:                      119        115   1.7us
+ * gsc_writeb status register:                      83         79   1.2us
+ * IBF to clear after sending SET_IM:             6204       6006    93us
+ * IBF to clear after sending LOAD_RT:            4467       4352    68us  
+ * IBF to clear after sending two LOAD_RTs:      18974      18859   295us
+ * READ_T1, read status/data, IRQ, call handler: 35564        n/a   556us
+ * cmd to ~IBF READ_T1 2nd time right after:   5158403        n/a    81ms
+ * between IRQ received and ~IBF for above:    2578877        n/a    40ms
+ *
+ * Performance stats after a run of this module configuring HIL and
+ * receiving a few mouse events:
+ *
+ * status in8  282508 cycles 7128 calls
+ * status out8   8404 cycles  341 calls
+ * data out8     1734 cycles   78 calls
+ * isr         174324 cycles  617 calls (includes take)
+ * take          1241 cycles    2 calls
+ * put        1411504 cycles 6937 calls
+ * task       1655209 cycles 6937 calls (includes put)
+ *
+ */
diff --git a/drivers/input/serio/hp_sdc_mlc.c b/drivers/input/serio/hp_sdc_mlc.c
new file mode 100644 (file)
index 0000000..e3c44ff
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * Access to HP-HIL MLC through HP System Device Controller.
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("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
+ *
+ * References:
+ * HP-HIL Technical Reference Manual.  Hewlett Packard Product No. 45918A
+ * System Device Controller Microprocessor Firmware Theory of Operation
+ *      for Part Number 1820-4784 Revision B.  Dwg No. A-1820-4784-2
+ *
+ */
+
+#include <linux/hil_mlc.h>
+#include <linux/hp_sdc.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#define PREFIX "HP SDC MLC: "
+
+static hil_mlc hp_sdc_mlc;
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("Glue for onboard HIL MLC in HP-PARISC machines");
+MODULE_LICENSE("Dual BSD/GPL");
+
+struct hp_sdc_mlc_priv_s {
+       int emtestmode;
+       hp_sdc_transaction trans;
+       u8 tseq[16];
+       int got5x;
+} hp_sdc_mlc_priv;
+
+/************************* Interrupt context ******************************/
+static void hp_sdc_mlc_isr (int irq, void *dev_id, 
+                           uint8_t status, uint8_t data) {
+       int idx;
+       hil_mlc *mlc = &hp_sdc_mlc;
+
+       write_lock(&(mlc->lock));
+       if (mlc->icount < 0) {
+               printk(KERN_WARNING PREFIX "HIL Overflow!\n");
+               up(&mlc->isem);
+               goto out;
+       }
+       idx = 15 - mlc->icount;
+       if ((status & HP_SDC_STATUS_IRQMASK) == HP_SDC_STATUS_HILDATA) {
+               mlc->ipacket[idx] |= data | HIL_ERR_INT;
+               mlc->icount--;
+               if (hp_sdc_mlc_priv.got5x) goto check;
+               if (!idx) goto check;
+               if ((mlc->ipacket[idx-1] & HIL_PKT_ADDR_MASK) !=
+                   (mlc->ipacket[idx] & HIL_PKT_ADDR_MASK)) {
+                       mlc->ipacket[idx] &= ~HIL_PKT_ADDR_MASK;
+                       mlc->ipacket[idx] |= (mlc->ipacket[idx-1] 
+                                                   & HIL_PKT_ADDR_MASK);
+               }
+               goto check;
+       }
+       /* We know status is 5X */
+       if (data & HP_SDC_HIL_ISERR) goto err;
+       mlc->ipacket[idx] = 
+               (data & HP_SDC_HIL_R1MASK) << HIL_PKT_ADDR_SHIFT;
+       hp_sdc_mlc_priv.got5x = 1;
+       goto out;
+
+ check:
+       hp_sdc_mlc_priv.got5x = 0;
+       if (mlc->imatch == 0) goto done;
+       if ((mlc->imatch == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) 
+           && (mlc->ipacket[idx] == (mlc->imatch | idx))) goto done;
+       if (mlc->ipacket[idx] == mlc->imatch) goto done;
+       goto out;
+
+ err:                          
+       printk(KERN_DEBUG PREFIX "err code %x\n", data);
+       switch (data) {
+       case HP_SDC_HIL_RC_DONE:
+               printk(KERN_WARNING PREFIX "Bastard SDC reconfigured loop!\n");
+               break;
+       case HP_SDC_HIL_ERR:
+               mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_PERR | 
+                 HIL_ERR_FERR | HIL_ERR_FOF;
+               break;
+       case HP_SDC_HIL_TO:
+               mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_LERR;
+               break;
+       case HP_SDC_HIL_RC:
+               printk(KERN_WARNING PREFIX "Bastard SDC decided to reconfigure loop!\n");
+               break;
+       default:
+               printk(KERN_WARNING PREFIX "Unkown HIL Error status (%x)!\n", data);
+               break;
+       }
+       /* No more data will be coming due to an error. */
+ done:
+       tasklet_schedule(mlc->tasklet);
+       up(&(mlc->isem));
+ out:
+       write_unlock(&(mlc->lock));
+}
+
+
+/******************** Tasklet or userspace context functions ****************/
+
+static int hp_sdc_mlc_in (hil_mlc *mlc, suseconds_t timeout) {
+       unsigned long flags;
+       struct hp_sdc_mlc_priv_s *priv;
+       int rc = 2;
+
+       priv = mlc->priv;
+
+       write_lock_irqsave(&(mlc->lock), flags);
+
+       /* Try to down the semaphore */
+       if (down_trylock(&(mlc->isem))) {
+               struct timeval tv;
+               if (priv->emtestmode) {
+                       mlc->ipacket[0] = 
+                               HIL_ERR_INT | (mlc->opacket & 
+                                              (HIL_PKT_CMD | 
+                                               HIL_PKT_ADDR_MASK | 
+                                               HIL_PKT_DATA_MASK));
+                       mlc->icount = 14;
+                       /* printk(KERN_DEBUG PREFIX ">[%x]\n", mlc->ipacket[0]); */
+                       goto wasup;
+               }
+               do_gettimeofday(&tv);
+               tv.tv_usec += 1000000 * (tv.tv_sec - mlc->instart.tv_sec);
+               if (tv.tv_usec - mlc->instart.tv_usec > mlc->intimeout) {
+                 /*              printk("!%i %i", 
+                                 tv.tv_usec - mlc->instart.tv_usec, 
+                                 mlc->intimeout);
+                 */
+                       rc = 1;
+                       up(&(mlc->isem));
+               }
+               goto done;
+       }
+ wasup:
+       up(&(mlc->isem));
+       rc = 0;
+       goto done;
+ done:
+       write_unlock_irqrestore(&(mlc->lock), flags);
+       return rc;
+}
+
+static int hp_sdc_mlc_cts (hil_mlc *mlc) {
+       struct hp_sdc_mlc_priv_s *priv;
+       unsigned long flags;
+
+       priv = mlc->priv;       
+
+       write_lock_irqsave(&(mlc->lock), flags);
+
+       /* Try to down the semaphores -- they should be up. */
+       if (down_trylock(&(mlc->isem))) {
+               BUG();
+               goto busy;
+       }
+       if (down_trylock(&(mlc->osem))) {
+               BUG();
+               up(&(mlc->isem));
+               goto busy;
+       }
+       up(&(mlc->isem));
+       up(&(mlc->osem));
+
+       if (down_trylock(&(mlc->csem))) {
+               if (priv->trans.act.semaphore != &(mlc->csem)) goto poll;
+               goto busy;
+       }
+       if (!(priv->tseq[4] & HP_SDC_USE_LOOP)) goto done;
+
+ poll:
+       priv->trans.act.semaphore = &(mlc->csem);
+       priv->trans.actidx = 0;
+       priv->trans.idx = 1;
+       priv->trans.endidx = 5;
+       priv->tseq[0] = 
+               HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE;
+       priv->tseq[1] = HP_SDC_CMD_READ_USE;
+       priv->tseq[2] = 1;
+       priv->tseq[3] = 0;
+       priv->tseq[4] = 0;
+       hp_sdc_enqueue_transaction(&(priv->trans));
+ busy:
+       write_unlock_irqrestore(&(mlc->lock), flags);
+       return 1;
+ done:
+       priv->trans.act.semaphore = &(mlc->osem);
+       up(&(mlc->csem));
+       write_unlock_irqrestore(&(mlc->lock), flags);
+       return 0;
+}
+
+static void hp_sdc_mlc_out (hil_mlc *mlc) {
+       struct hp_sdc_mlc_priv_s *priv;
+       unsigned long flags;
+
+       priv = mlc->priv;
+
+       write_lock_irqsave(&(mlc->lock), flags);
+       
+       /* Try to down the semaphore -- it should be up. */
+       if (down_trylock(&(mlc->osem))) {
+               BUG();
+               goto done;
+       }
+
+       if (mlc->opacket & HIL_DO_ALTER_CTRL) goto do_control;
+
+ do_data:
+       if (priv->emtestmode) {
+               up(&(mlc->osem));
+               goto done;
+       }
+       /* Shouldn't be sending commands when loop may be busy */
+       if (down_trylock(&(mlc->csem))) {
+               BUG();
+               goto done;
+       }
+       up(&(mlc->csem));
+
+       priv->trans.actidx = 0;
+       priv->trans.idx = 1;
+       priv->trans.act.semaphore = &(mlc->osem);
+       priv->trans.endidx = 6;
+       priv->tseq[0] = 
+               HP_SDC_ACT_DATAREG | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_SEMAPHORE;
+       priv->tseq[1] = 0x7;
+       priv->tseq[2] = 
+               (mlc->opacket & 
+                (HIL_PKT_ADDR_MASK | HIL_PKT_CMD))
+                  >> HIL_PKT_ADDR_SHIFT;
+       priv->tseq[3] = 
+               (mlc->opacket & HIL_PKT_DATA_MASK) 
+                 >> HIL_PKT_DATA_SHIFT;
+       priv->tseq[4] = 0;  /* No timeout */
+       if (priv->tseq[3] == HIL_CMD_DHR) priv->tseq[4] = 1;
+       priv->tseq[5] = HP_SDC_CMD_DO_HIL;
+       goto enqueue;
+
+ do_control:
+       priv->emtestmode = mlc->opacket & HIL_CTRL_TEST;
+       if ((mlc->opacket & (HIL_CTRL_APE | HIL_CTRL_IPF)) == HIL_CTRL_APE) {
+               BUG(); /* we cannot emulate this, it should not be used. */
+       }
+       if ((mlc->opacket & HIL_CTRL_ONLY) == HIL_CTRL_ONLY) goto control_only;
+       if (mlc->opacket & HIL_CTRL_APE) { 
+               BUG(); /* Should not send command/data after engaging APE */
+               goto done;
+       }
+       /* Disengaging APE this way would not be valid either since 
+        * the loop must be allowed to idle.
+        *
+        * So, it works out that we really never actually send control 
+        * and data when using SDC, we just send the data. 
+        */
+       goto do_data;
+
+ control_only:
+       priv->trans.actidx = 0;
+       priv->trans.idx = 1;
+       priv->trans.act.semaphore = &(mlc->osem);
+       priv->trans.endidx = 4;
+       priv->tseq[0] = 
+         HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE;
+       priv->tseq[1] = HP_SDC_CMD_SET_LPC;
+       priv->tseq[2] = 1;
+       //      priv->tseq[3] = (mlc->ddc + 1) | HP_SDC_LPS_ACSUCC;
+       priv->tseq[3] = 0;
+       if (mlc->opacket & HIL_CTRL_APE) {
+               priv->tseq[3] |= HP_SDC_LPC_APE_IPF;
+               down_trylock(&(mlc->csem));
+       } 
+ enqueue:
+       hp_sdc_enqueue_transaction(&(priv->trans));
+ done:
+       write_unlock_irqrestore(&(mlc->lock), flags);
+}
+
+static int __init hp_sdc_mlc_init(void)
+{
+       hil_mlc *mlc = &hp_sdc_mlc;
+
+       printk(KERN_INFO PREFIX "Registering the System Domain Controller's HIL MLC.\n");
+
+       hp_sdc_mlc_priv.emtestmode = 0;
+       hp_sdc_mlc_priv.trans.seq = hp_sdc_mlc_priv.tseq;
+       hp_sdc_mlc_priv.trans.act.semaphore = &(mlc->osem);
+       hp_sdc_mlc_priv.got5x = 0;
+
+       mlc->cts                = &hp_sdc_mlc_cts;
+       mlc->in                 = &hp_sdc_mlc_in;
+       mlc->out                = &hp_sdc_mlc_out;
+
+       if (hil_mlc_register(mlc)) {
+               printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n");
+               goto err0;
+       }
+       mlc->priv               = &hp_sdc_mlc_priv;
+
+       if (hp_sdc_request_hil_irq(&hp_sdc_mlc_isr)) {
+               printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\n");
+               goto err1;
+       }
+       return 0;
+ err1:
+       if (hil_mlc_unregister(mlc)) {
+               printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
+                       "This is bad.  Could cause an oops.\n");
+       }
+ err0:
+       return -EBUSY;
+}
+
+static void __exit hp_sdc_mlc_exit(void)
+{
+       hil_mlc *mlc = &hp_sdc_mlc;
+       if (hp_sdc_release_hil_irq(&hp_sdc_mlc_isr)) {
+               printk(KERN_ERR PREFIX "Failed to release the raw HIL ISR hook.\n"
+                       "This is bad.  Could cause an oops.\n");
+       }
+       if (hil_mlc_unregister(mlc)) {
+               printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
+                       "This is bad.  Could cause an oops.\n");
+       }
+}
+
+module_init(hp_sdc_mlc_init);
+module_exit(hp_sdc_mlc_exit);
diff --git a/drivers/input/touchscreen/corgi_ts.c b/drivers/input/touchscreen/corgi_ts.c
new file mode 100644 (file)
index 0000000..3f8b61c
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ *  Touchscreen driver for Sharp Corgi models (SL-C7xx)
+ *
+ *  Copyright (c) 2004-2005 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/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/irq.h>
+
+#include <asm/arch/corgi.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+
+#define PWR_MODE_ACTIVE                0
+#define PWR_MODE_SUSPEND       1
+
+#define X_AXIS_MAX             3830
+#define X_AXIS_MIN             150
+#define Y_AXIS_MAX             3830
+#define Y_AXIS_MIN             190
+#define PRESSURE_MIN           0
+#define PRESSURE_MAX           15000
+
+struct ts_event {
+       short pressure;
+       short x;
+       short y;
+};
+
+struct corgi_ts {
+       char phys[32];
+       struct input_dev input;
+       struct timer_list timer;
+       struct ts_event tc;
+       int pendown;
+       int power_mode;
+};
+
+#define STATUS_HSYNC           (GPLR(CORGI_GPIO_HSYNC) & GPIO_bit(CORGI_GPIO_HSYNC))
+
+#define SyncHS()       while((STATUS_HSYNC) == 0); while((STATUS_HSYNC) != 0);
+#define CCNT(a)                asm volatile ("mrc p14, 0, %0, C1, C0, 0" : "=r"(a))
+#define CCNT_ON()      {int pmnc = 1; asm volatile ("mcr p14, 0, %0, C0, C0, 0" : : "r"(pmnc));}
+#define CCNT_OFF()     {int pmnc = 0; asm volatile ("mcr p14, 0, %0, C0, C0, 0" : : "r"(pmnc));}
+
+#define WAIT_HS_400_VGA                7013U   // 17.615us
+#define WAIT_HS_400_QVGA       16622U  // 41.750us
+
+
+/* ADS7846 Touch Screen Controller bit definitions */
+#define ADSCTRL_PD0            (1u << 0)       /* PD0 */
+#define ADSCTRL_PD1            (1u << 1)       /* PD1 */
+#define ADSCTRL_DFR            (1u << 2)       /* SER/DFR */
+#define ADSCTRL_MOD            (1u << 3)       /* Mode */
+#define ADSCTRL_ADR_SH 4       /* Address setting */
+#define ADSCTRL_STS            (1u << 7)       /* Start Bit */
+
+/* External Functions */
+extern int w100fb_get_xres(void);
+extern int w100fb_get_blanking(void);
+extern int w100fb_get_fastsysclk(void);
+extern unsigned int get_clk_frequency_khz(int info);
+
+static unsigned long calc_waittime(void)
+{
+       int w100fb_xres = w100fb_get_xres();
+       unsigned int waittime = 0;
+
+       if (w100fb_xres == 480 || w100fb_xres == 640) {
+               waittime = WAIT_HS_400_VGA * get_clk_frequency_khz(0) / 398131U;
+
+               if (w100fb_get_fastsysclk() == 100)
+                       waittime = waittime * 75 / 100;
+
+               if (w100fb_xres == 640)
+                       waittime *= 3;
+
+               return waittime;
+       }
+
+       return WAIT_HS_400_QVGA * get_clk_frequency_khz(0) / 398131U;
+}
+
+static int sync_receive_data_send_cmd(int doRecive, int doSend, unsigned int address, unsigned long wait_time)
+{
+       int pos = 0;
+       unsigned long timer1 = 0, timer2;
+       int dosleep;
+
+       dosleep = !w100fb_get_blanking();
+
+       if (dosleep && doSend) {
+               CCNT_ON();
+               /* polling HSync */
+               SyncHS();
+               /* get CCNT */
+               CCNT(timer1);
+       }
+
+       if (doRecive)
+               pos = corgi_ssp_ads7846_get();
+
+       if (doSend) {
+               int cmd = ADSCTRL_PD0 | ADSCTRL_PD1 | (address << ADSCTRL_ADR_SH) | ADSCTRL_STS;
+               /* dummy command */
+               corgi_ssp_ads7846_put(cmd);
+               corgi_ssp_ads7846_get();
+
+               if (dosleep) {
+                       /* Wait after HSync */
+                       CCNT(timer2);
+                       if (timer2-timer1 > wait_time) {
+                               /* timeout */
+                               SyncHS();
+                               /* get OSCR */
+                               CCNT(timer1);
+                               /* Wait after HSync */
+                               CCNT(timer2);
+                       }
+                       while (timer2 - timer1 < wait_time)
+                               CCNT(timer2);
+               }
+               corgi_ssp_ads7846_put(cmd);
+               if (dosleep)
+                       CCNT_OFF();
+       }
+       return pos;
+}
+
+static int read_xydata(struct corgi_ts *corgi_ts)
+{
+       unsigned int x, y, z1, z2;
+       unsigned long flags, wait_time;
+
+       /* critical section */
+       local_irq_save(flags);
+       corgi_ssp_ads7846_lock();
+       wait_time=calc_waittime();
+
+       /* Y-axis */
+       sync_receive_data_send_cmd(0, 1, 1u, wait_time);
+
+       /* Y-axis */
+       sync_receive_data_send_cmd(1, 1, 1u, wait_time);
+
+       /* X-axis */
+       y = sync_receive_data_send_cmd(1, 1, 5u, wait_time);
+
+       /* Z1 */
+       x = sync_receive_data_send_cmd(1, 1, 3u, wait_time);
+
+       /* Z2 */
+       z1 = sync_receive_data_send_cmd(1, 1, 4u, wait_time);
+       z2 = sync_receive_data_send_cmd(1, 0, 4u, wait_time);
+
+       /* Power-Down Enable */
+       corgi_ssp_ads7846_put((1u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
+       corgi_ssp_ads7846_get();
+
+       corgi_ssp_ads7846_unlock();
+       local_irq_restore(flags);
+
+       if (x== 0 || y == 0 || z1 == 0 || (x * (z2 - z1) / z1) >= 15000) {
+               corgi_ts->tc.pressure = 0;
+               return 0;
+       }
+
+       corgi_ts->tc.x = x;
+       corgi_ts->tc.y = y;
+       corgi_ts->tc.pressure = (x * (z2 - z1)) / z1;
+       return 1;
+}
+
+static void new_data(struct corgi_ts *corgi_ts, struct pt_regs *regs)
+{
+       if (corgi_ts->power_mode != PWR_MODE_ACTIVE)
+               return;
+
+       if (!corgi_ts->tc.pressure && corgi_ts->pendown == 0)
+               return;
+
+       if (regs)
+               input_regs(&corgi_ts->input, regs);
+
+       input_report_abs(&corgi_ts->input, ABS_X, corgi_ts->tc.x);
+       input_report_abs(&corgi_ts->input, ABS_Y, corgi_ts->tc.y);
+       input_report_abs(&corgi_ts->input, ABS_PRESSURE, corgi_ts->tc.pressure);
+       input_report_key(&corgi_ts->input, BTN_TOUCH, (corgi_ts->pendown != 0));
+       input_sync(&corgi_ts->input);
+}
+
+static void ts_interrupt_main(struct corgi_ts *corgi_ts, int isTimer, struct pt_regs *regs)
+{
+       if ((GPLR(CORGI_GPIO_TP_INT) & GPIO_bit(CORGI_GPIO_TP_INT)) == 0) {
+               /* Disable Interrupt */
+               set_irq_type(CORGI_IRQ_GPIO_TP_INT, IRQT_NOEDGE);
+               if (read_xydata(corgi_ts)) {
+                       corgi_ts->pendown = 1;
+                       new_data(corgi_ts, regs);
+               }
+               mod_timer(&corgi_ts->timer, jiffies + HZ / 100);
+       } else {
+               if (corgi_ts->pendown == 1 || corgi_ts->pendown == 2) {
+                       mod_timer(&corgi_ts->timer, jiffies + HZ / 100);
+                       corgi_ts->pendown++;
+                       return;
+               }
+
+               if (corgi_ts->pendown) {
+                       corgi_ts->tc.pressure = 0;
+                       new_data(corgi_ts, regs);
+               }
+
+               /* Enable Falling Edge */
+               set_irq_type(CORGI_IRQ_GPIO_TP_INT, IRQT_FALLING);
+               corgi_ts->pendown = 0;
+       }
+}
+
+static void corgi_ts_timer(unsigned long data)
+{
+       struct corgi_ts *corgits_data = (struct corgi_ts *) data;
+       ts_interrupt_main(corgits_data, 1, NULL);
+}
+
+static irqreturn_t ts_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct corgi_ts *corgits_data = dev_id;
+       ts_interrupt_main(corgits_data, 0, regs);
+       return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM
+static int corgits_suspend(struct device *dev, uint32_t state, uint32_t level)
+{
+       if (level == SUSPEND_POWER_DOWN) {
+               struct corgi_ts *corgi_ts = dev_get_drvdata(dev);
+
+               if (corgi_ts->pendown) {
+                       del_timer_sync(&corgi_ts->timer);
+                       corgi_ts->tc.pressure = 0;
+                       new_data(corgi_ts, NULL);
+                       corgi_ts->pendown = 0;
+               }
+               corgi_ts->power_mode = PWR_MODE_SUSPEND;
+
+               corgi_ssp_ads7846_putget((1u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
+       }
+       return 0;
+}
+
+static int corgits_resume(struct device *dev, uint32_t level)
+{
+       if (level == RESUME_POWER_ON) {
+               struct corgi_ts *corgi_ts = dev_get_drvdata(dev);
+
+               corgi_ssp_ads7846_putget((4u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
+               /* Enable Falling Edge */
+               set_irq_type(CORGI_IRQ_GPIO_TP_INT, IRQT_FALLING);
+               corgi_ts->power_mode = PWR_MODE_ACTIVE;
+       }
+       return 0;
+}
+#else
+#define corgits_suspend                NULL
+#define corgits_resume         NULL
+#endif
+
+static int __init corgits_probe(struct device *dev)
+{
+       struct corgi_ts *corgi_ts;
+
+       if (!(corgi_ts = kmalloc(sizeof(struct corgi_ts), GFP_KERNEL)))
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, corgi_ts);
+
+       memset(corgi_ts, 0, sizeof(struct corgi_ts));
+
+       init_input_dev(&corgi_ts->input);
+       corgi_ts->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+       corgi_ts->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+       input_set_abs_params(&corgi_ts->input, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0);
+       input_set_abs_params(&corgi_ts->input, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0);
+       input_set_abs_params(&corgi_ts->input, ABS_PRESSURE, PRESSURE_MIN, PRESSURE_MAX, 0, 0);
+
+       strcpy(corgi_ts->phys, "corgits/input0");
+
+       corgi_ts->input.private = corgi_ts;
+       corgi_ts->input.name = "Corgi Touchscreen";
+       corgi_ts->input.dev = dev;
+       corgi_ts->input.phys = corgi_ts->phys;
+       corgi_ts->input.id.bustype = BUS_HOST;
+       corgi_ts->input.id.vendor = 0x0001;
+       corgi_ts->input.id.product = 0x0002;
+       corgi_ts->input.id.version = 0x0100;
+
+       pxa_gpio_mode(CORGI_GPIO_TP_INT | GPIO_IN);
+       pxa_gpio_mode(CORGI_GPIO_HSYNC | GPIO_IN);
+
+       /* Initiaize ADS7846 Difference Reference mode */
+       corgi_ssp_ads7846_putget((1u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
+       mdelay(5);
+       corgi_ssp_ads7846_putget((3u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
+       mdelay(5);
+       corgi_ssp_ads7846_putget((4u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
+       mdelay(5);
+       corgi_ssp_ads7846_putget((5u << ADSCTRL_ADR_SH) | ADSCTRL_STS);
+       mdelay(5);
+
+       init_timer(&corgi_ts->timer);
+       corgi_ts->timer.data = (unsigned long) corgi_ts;
+       corgi_ts->timer.function = corgi_ts_timer;
+
+       input_register_device(&corgi_ts->input);
+       corgi_ts->power_mode = PWR_MODE_ACTIVE;
+
+       if (request_irq(CORGI_IRQ_GPIO_TP_INT, ts_interrupt, SA_INTERRUPT, "ts", corgi_ts)) {
+               input_unregister_device(&corgi_ts->input);
+               kfree(corgi_ts);
+               return -EBUSY;
+       }
+
+       /* Enable Falling Edge */
+       set_irq_type(CORGI_IRQ_GPIO_TP_INT, IRQT_FALLING);
+
+       printk(KERN_INFO "input: Corgi Touchscreen Registered\n");
+
+       return 0;
+}
+
+static int corgits_remove(struct device *dev)
+{
+       struct corgi_ts *corgi_ts = dev_get_drvdata(dev);
+
+       free_irq(CORGI_IRQ_GPIO_TP_INT, NULL);
+       del_timer_sync(&corgi_ts->timer);
+       input_unregister_device(&corgi_ts->input);
+       kfree(corgi_ts);
+       return 0;
+}
+
+static struct device_driver corgits_driver = {
+       .name           = "corgi-ts",
+       .bus            = &platform_bus_type,
+       .probe          = corgits_probe,
+       .remove         = corgits_remove,
+       .suspend        = corgits_suspend,
+       .resume         = corgits_resume,
+};
+
+static int __devinit corgits_init(void)
+{
+       return driver_register(&corgits_driver);
+}
+
+static void __exit corgits_exit(void)
+{
+       driver_unregister(&corgits_driver);
+}
+
+module_init(corgits_init);
+module_exit(corgits_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
+MODULE_DESCRIPTION("Corgi TouchScreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c
new file mode 100644 (file)
index 0000000..546ce59
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * Elo serial touchscreen driver
+ *
+ * Copyright (c) 2004 Vojtech Pavlik
+ */
+
+/*
+ * 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 can handle serial Elo touchscreens using either the Elo standard
+ * 'E271-2210' 10-byte protocol, Elo legacy 'E281A-4002' 6-byte protocol, Elo
+ * legacy 'E271-140' 4-byte protocol and Elo legacy 'E261-280' 3-byte protocol.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC    "Elo serial touchscreen driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define        ELO_MAX_LENGTH  10
+
+static char *elo_name = "Elo Serial TouchScreen";
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct elo {
+       struct input_dev dev;
+       struct serio *serio;
+       int id;
+       int idx;
+       unsigned char csum;
+       unsigned char data[ELO_MAX_LENGTH];
+       char phys[32];
+};
+
+static void elo_process_data_10(struct elo* elo, unsigned char data, struct pt_regs *regs)
+{
+       struct input_dev *dev = &elo->dev;
+
+       elo->csum += elo->data[elo->idx] = data;
+
+       switch (elo->idx++) {
+
+               case 0:
+                       if (data != 'U') {
+                               elo->idx = 0;
+                               elo->csum = 0;
+                       }
+                       break;
+
+               case 1:
+                       if (data != 'T') {
+                               elo->idx = 0;
+                               elo->csum = 0;
+                       }
+                       break;
+
+               case 9:
+                       if (elo->csum) {
+                               input_regs(dev, regs);
+                               input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]);
+                               input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]);
+                               input_report_abs(dev, ABS_PRESSURE, (elo->data[8] << 8) | elo->data[7]);
+                               input_report_key(dev, BTN_TOUCH, elo->data[2] & 3);
+                               input_sync(dev);
+                       }
+                       elo->idx = 0;
+                       elo->csum = 0;
+                       break;
+       }
+}
+
+static void elo_process_data_6(struct elo* elo, unsigned char data, struct pt_regs *regs)
+{
+       struct input_dev *dev = &elo->dev;
+
+       elo->data[elo->idx] = data;
+
+       switch (elo->idx++) {
+
+               case 0: if ((data & 0xc0) != 0xc0) elo->idx = 0; break;
+               case 1: if ((data & 0xc0) != 0x80) elo->idx = 0; break;
+               case 2: if ((data & 0xc0) != 0x40) elo->idx = 0; break;
+
+               case 3:
+                       if (data & 0xc0) {
+                               elo->idx = 0;
+                               break;
+                       }
+
+                       input_regs(dev, regs);
+                       input_report_abs(dev, ABS_X, ((elo->data[0] & 0x3f) << 6) | (elo->data[1] & 0x3f));
+                       input_report_abs(dev, ABS_Y, ((elo->data[2] & 0x3f) << 6) | (elo->data[3] & 0x3f));
+
+                       if (elo->id == 2) {
+                               input_report_key(dev, BTN_TOUCH, 1);
+                               input_sync(dev);
+                               elo->idx = 0;
+                       }
+
+                       break;
+
+               case 4:
+                       if (data) {
+                               input_sync(dev);
+                               elo->idx = 0;
+                       }
+                       break;
+
+               case 5:
+                       if ((data & 0xf0) == 0) {
+                               input_report_abs(dev, ABS_PRESSURE, elo->data[5]);
+                               input_report_key(dev, BTN_TOUCH, elo->data[5]);
+                       }
+                       input_sync(dev);
+                       elo->idx = 0;
+                       break;
+       }
+}
+
+static void elo_process_data_3(struct elo* elo, unsigned char data, struct pt_regs *regs)
+{
+       struct input_dev *dev = &elo->dev;
+
+       elo->data[elo->idx] = data;
+
+       switch (elo->idx++) {
+
+               case 0:
+                       if ((data & 0x7f) != 0x01)
+                               elo->idx = 0;
+                       break;
+               case 2:
+                       input_regs(dev, regs);
+                       input_report_key(dev, BTN_TOUCH, !(elo->data[1] & 0x80));
+                       input_report_abs(dev, ABS_X, elo->data[1]);
+                       input_report_abs(dev, ABS_Y, elo->data[2]);
+                       input_sync(dev);
+                       elo->idx = 0;
+                       break;
+       }
+}
+
+static irqreturn_t elo_interrupt(struct serio *serio,
+               unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+       struct elo* elo = serio_get_drvdata(serio);
+
+       switch(elo->id) {
+               case 0:
+                       elo_process_data_10(elo, data, regs);
+                       break;
+
+               case 1:
+               case 2:
+                       elo_process_data_6(elo, data, regs);
+                       break;
+
+               case 3:
+                       elo_process_data_3(elo, data, regs);
+                       break;
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * elo_disconnect() is the opposite of elo_connect()
+ */
+
+static void elo_disconnect(struct serio *serio)
+{
+       struct elo* elo = serio_get_drvdata(serio);
+
+       input_unregister_device(&elo->dev);
+       serio_close(serio);
+       serio_set_drvdata(serio, NULL);
+       kfree(elo);
+}
+
+/*
+ * elo_connect() is the routine that is called when someone adds a
+ * new serio device that supports Gunze protocol and registers it as
+ * an input device.
+ */
+
+static int elo_connect(struct serio *serio, struct serio_driver *drv)
+{
+       struct elo *elo;
+       int err;
+
+       if (!(elo = kmalloc(sizeof(struct elo), GFP_KERNEL)))
+               return -ENOMEM;
+
+       memset(elo, 0, sizeof(struct elo));
+
+       init_input_dev(&elo->dev);
+       elo->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+       elo->dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+
+       elo->id = serio->id.id;
+
+       switch (elo->id) {
+
+               case 0: /* 10-byte protocol */
+                       input_set_abs_params(&elo->dev, ABS_X, 96, 4000, 0, 0);
+                       input_set_abs_params(&elo->dev, ABS_Y, 96, 4000, 0, 0);
+                       input_set_abs_params(&elo->dev, ABS_PRESSURE, 0, 255, 0, 0);
+                       break;
+               
+               case 1: /* 6-byte protocol */
+                       input_set_abs_params(&elo->dev, ABS_PRESSURE, 0, 15, 0, 0);
+
+               case 2: /* 4-byte protocol */
+                       input_set_abs_params(&elo->dev, ABS_X, 96, 4000, 0, 0);
+                       input_set_abs_params(&elo->dev, ABS_Y, 96, 4000, 0, 0);
+                       break;
+
+               case 3: /* 3-byte protocol */
+                       input_set_abs_params(&elo->dev, ABS_X, 0, 255, 0, 0);
+                       input_set_abs_params(&elo->dev, ABS_Y, 0, 255, 0, 0);
+                       break;
+       }
+
+       elo->serio = serio;
+
+       sprintf(elo->phys, "%s/input0", serio->phys);
+
+       elo->dev.private = elo;
+       elo->dev.name = elo_name;
+       elo->dev.phys = elo->phys;
+       elo->dev.id.bustype = BUS_RS232;
+       elo->dev.id.vendor = SERIO_ELO;
+       elo->dev.id.product = elo->id;
+       elo->dev.id.version = 0x0100;
+
+       serio_set_drvdata(serio, elo);
+
+       err = serio_open(serio, drv);
+       if (err) {
+               serio_set_drvdata(serio, NULL);
+               kfree(elo);
+               return err;
+       }
+
+       input_register_device(&elo->dev);
+
+       printk(KERN_INFO "input: %s on %s\n", elo_name, serio->phys);
+
+       return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id elo_serio_ids[] = {
+       {
+               .type   = SERIO_RS232,
+               .proto  = SERIO_ELO,
+               .id     = SERIO_ANY,
+               .extra  = SERIO_ANY,
+       },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, elo_serio_ids);
+
+static struct serio_driver elo_drv = {
+       .driver         = {
+               .name   = "elo",
+       },
+       .description    = DRIVER_DESC,
+       .id_table       = elo_serio_ids,
+       .interrupt      = elo_interrupt,
+       .connect        = elo_connect,
+       .disconnect     = elo_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init elo_init(void)
+{
+       serio_register_driver(&elo_drv);
+       return 0;
+}
+
+static void __exit elo_exit(void)
+{
+       serio_unregister_driver(&elo_drv);
+}
+
+module_init(elo_init);
+module_exit(elo_exit);
diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c
new file mode 100644 (file)
index 0000000..7e14044
--- /dev/null
@@ -0,0 +1,135 @@
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <asm/adc.h>
+#include <asm/hp6xx/hp6xx.h>
+
+#define MODNAME "hp680_ts_input"
+
+#define HP680_TS_ABS_X_MIN     40
+#define HP680_TS_ABS_X_MAX     950
+#define HP680_TS_ABS_Y_MIN     80
+#define HP680_TS_ABS_Y_MAX     910
+
+#define        SCPCR   0xa4000116
+#define        PHDR    0xa400012e
+#define SCPDR  0xa4000136
+
+static void do_softint(void *data);
+
+static struct input_dev hp680_ts_dev;
+static DECLARE_WORK(work, do_softint, 0);
+static char *hp680_ts_name = "HP Jornada touchscreen";
+static char *hp680_ts_phys = "input0";
+
+static void do_softint(void *data)
+{
+       int absx = 0, absy = 0;
+       u8 scpdr;
+       int touched = 0;
+
+       if (ctrl_inb(PHDR) & PHDR_TS_PEN_DOWN) {
+               scpdr = ctrl_inb(SCPDR);
+               scpdr |= SCPDR_TS_SCAN_ENABLE;
+               scpdr &= ~SCPDR_TS_SCAN_Y;
+               ctrl_outb(scpdr, SCPDR);
+               udelay(30);
+
+               absy = adc_single(ADC_CHANNEL_TS_Y);
+
+               scpdr = ctrl_inb(SCPDR);
+               scpdr |= SCPDR_TS_SCAN_Y;
+               scpdr &= ~SCPDR_TS_SCAN_X;
+               ctrl_outb(scpdr, SCPDR);
+               udelay(30);
+
+               absx = adc_single(ADC_CHANNEL_TS_X);
+
+               scpdr = ctrl_inb(SCPDR);
+               scpdr |= SCPDR_TS_SCAN_X;
+               scpdr &= ~SCPDR_TS_SCAN_ENABLE;
+               ctrl_outb(scpdr, SCPDR);
+               udelay(100);
+               touched = ctrl_inb(PHDR) & PHDR_TS_PEN_DOWN;
+       }
+
+       if (touched) {
+               input_report_key(&hp680_ts_dev, BTN_TOUCH, 1);
+               input_report_abs(&hp680_ts_dev, ABS_X, absx);
+               input_report_abs(&hp680_ts_dev, ABS_Y, absy);
+       } else {
+               input_report_key(&hp680_ts_dev, BTN_TOUCH, 0);
+       }
+
+       input_sync(&hp680_ts_dev);
+       enable_irq(HP680_TS_IRQ);
+}
+
+static irqreturn_t hp680_ts_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+       disable_irq_nosync(irq);
+       schedule_delayed_work(&work, HZ / 20);
+
+       return IRQ_HANDLED;
+}
+
+static int __init hp680_ts_init(void)
+{
+       u8 scpdr;
+       u16 scpcr;
+
+       scpdr = ctrl_inb(SCPDR);
+       scpdr |= SCPDR_TS_SCAN_X | SCPDR_TS_SCAN_Y;
+       scpdr &= ~SCPDR_TS_SCAN_ENABLE;
+       ctrl_outb(scpdr, SCPDR);
+
+       scpcr = ctrl_inw(SCPCR);
+       scpcr &= ~SCPCR_TS_MASK;
+       scpcr |= SCPCR_TS_ENABLE;
+       ctrl_outw(scpcr, SCPCR);
+
+       memset(&hp680_ts_dev, 0, sizeof(hp680_ts_dev));
+       init_input_dev(&hp680_ts_dev);
+
+       hp680_ts_dev.evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
+       hp680_ts_dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y);
+       hp680_ts_dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+
+       hp680_ts_dev.absmin[ABS_X] = HP680_TS_ABS_X_MIN;
+       hp680_ts_dev.absmin[ABS_Y] = HP680_TS_ABS_Y_MIN;
+       hp680_ts_dev.absmax[ABS_X] = HP680_TS_ABS_X_MAX;
+       hp680_ts_dev.absmax[ABS_Y] = HP680_TS_ABS_Y_MAX;
+
+       hp680_ts_dev.name = hp680_ts_name;
+       hp680_ts_dev.phys = hp680_ts_phys;
+       input_register_device(&hp680_ts_dev);
+
+       if (request_irq
+           (HP680_TS_IRQ, hp680_ts_interrupt, SA_INTERRUPT, MODNAME, 0) < 0) {
+               printk(KERN_ERR "hp680_touchscreen.c : Can't allocate irq %d\n",
+                      HP680_TS_IRQ);
+               input_unregister_device(&hp680_ts_dev);
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static void __exit hp680_ts_exit(void)
+{
+       free_irq(HP680_TS_IRQ, 0);
+       cancel_delayed_work(&work);
+       flush_scheduled_work();
+       input_unregister_device(&hp680_ts_dev);
+}
+
+module_init(hp680_ts_init);
+module_exit(hp680_ts_exit);
+
+MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
+MODULE_DESCRIPTION("HP Jornada 680 touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mk712.c b/drivers/input/touchscreen/mk712.c
new file mode 100644 (file)
index 0000000..2d14a57
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * ICS MK712 touchscreen controller driver
+ *
+ * Copyright (c) 1999-2002 Transmeta Corporation
+ * Copyright (c) 2005 Rick Koch <n1gp@hotmail.com>
+ * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz>
+ */
+
+/*
+ * 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 ICS MicroClock MK712 TouchScreen controller,
+ * found in Gateway AOL Connected Touchpad computers.
+ *
+ * Documentation for ICS MK712 can be found at:
+ *     http://www.icst.com/pdf/mk712.pdf
+ */
+
+/*
+ * 1999-12-18: original version, Daniel Quinlan
+ * 1999-12-19: added anti-jitter code, report pen-up events, fixed mk712_poll
+ *             to use queue_empty, Nathan Laredo
+ * 1999-12-20: improved random point rejection, Nathan Laredo
+ * 2000-01-05: checked in new anti-jitter code, changed mouse protocol, fixed
+ *             queue code, added module options, other fixes, Daniel Quinlan
+ * 2002-03-15: Clean up for kernel merge <alan@redhat.com>
+ *             Fixed multi open race, fixed memory checks, fixed resource
+ *             allocation, fixed close/powerdown bug, switched to new init
+ * 2005-01-18: Ported to 2.6 from 2.4.28, Rick Koch
+ * 2005-02-05: Rewritten for the input layer, Vojtech Pavlik
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Daniel Quinlan <quinlan@pathname.com>, Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION("ICS MicroClock MK712 TouchScreen driver");
+MODULE_LICENSE("GPL");
+
+static unsigned int mk712_io = 0x260;  /* Also 0x200, 0x208, 0x300 */
+module_param_named(io, mk712_io, uint, 0);
+MODULE_PARM_DESC(io, "I/O base address of MK712 touchscreen controller");
+
+static unsigned int mk712_irq = 10;    /* Also 12, 14, 15 */
+module_param_named(irq, mk712_irq, uint, 0);
+MODULE_PARM_DESC(irq, "IRQ of MK712 touchscreen controller");
+
+/* eight 8-bit registers */
+#define MK712_STATUS           0
+#define MK712_X                        2
+#define MK712_Y                        4
+#define MK712_CONTROL          6
+#define MK712_RATE             7
+
+/* status */
+#define        MK712_STATUS_TOUCH                      0x10
+#define        MK712_CONVERSION_COMPLETE               0x80
+
+/* control */
+#define MK712_ENABLE_INT                       0x01
+#define MK712_INT_ON_CONVERSION_COMPLETE       0x02
+#define MK712_INT_ON_CHANGE_IN_TOUCH_STATUS    0x04
+#define MK712_ENABLE_PERIODIC_CONVERSIONS      0x10
+#define MK712_READ_ONE_POINT                   0x20
+#define MK712_POWERUP                          0x40
+
+static int mk712_used = 0;
+static struct input_dev mk712_dev;
+static DEFINE_SPINLOCK(mk712_lock);
+
+static irqreturn_t mk712_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       unsigned char status;
+       static int debounce = 1;
+       static unsigned short last_x;
+       static unsigned short last_y;
+
+       spin_lock(&mk712_lock);
+       input_regs(&mk712_dev, regs);
+
+       status = inb(mk712_io + MK712_STATUS);
+
+       if (~status & MK712_CONVERSION_COMPLETE) {
+               debounce = 1;
+               goto end;
+       }
+
+       if (~status & MK712_STATUS_TOUCH)
+       {
+               debounce = 1;
+               input_report_key(&mk712_dev, BTN_TOUCH, 0);
+               goto end;
+       }
+
+       if (debounce)
+       {
+               debounce = 0;
+               goto end;
+       }
+
+       input_report_key(&mk712_dev, BTN_TOUCH, 1);
+       input_report_abs(&mk712_dev, ABS_X, last_x);
+       input_report_abs(&mk712_dev, ABS_Y, last_y);
+
+end:
+
+       last_x = inw(mk712_io + MK712_X) & 0x0fff;
+       last_y = inw(mk712_io + MK712_Y) & 0x0fff;
+       input_sync(&mk712_dev);
+       spin_unlock(&mk712_lock);
+       return IRQ_HANDLED;
+}
+
+static int mk712_open(struct input_dev *dev)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&mk712_lock, flags);
+
+       if (!mk712_used++) {
+
+               outb(0, mk712_io + MK712_CONTROL); /* Reset */
+
+               outb(MK712_ENABLE_INT | MK712_INT_ON_CONVERSION_COMPLETE |
+                       MK712_INT_ON_CHANGE_IN_TOUCH_STATUS |
+                       MK712_ENABLE_PERIODIC_CONVERSIONS |
+                       MK712_POWERUP, mk712_io + MK712_CONTROL);
+
+               outb(10, mk712_io + MK712_RATE); /* 187 points per second */
+       }
+
+       spin_unlock_irqrestore(&mk712_lock, flags);
+
+       return 0;
+}
+
+static void mk712_close(struct input_dev *dev)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&mk712_lock, flags);
+
+       if (!--mk712_used)
+               outb(0, mk712_io + MK712_CONTROL);
+
+       spin_unlock_irqrestore(&mk712_lock, flags);
+}
+
+static struct input_dev mk712_dev = {
+       .evbit   = { BIT(EV_KEY) | BIT(EV_ABS) },
+       .keybit  = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
+       .absbit  = { BIT(ABS_X) | BIT(ABS_Y) },
+       .open    = mk712_open,
+       .close   = mk712_close,
+       .name    = "ICS MicroClock MK712 TouchScreen",
+       .phys    = "isa0260/input0",
+       .absmin  = { [ABS_X] = 0, [ABS_Y] = 0 },
+       .absmax  = { [ABS_X] = 0xfff, [ABS_Y] = 0xfff },
+       .absfuzz = { [ABS_X] = 88, [ABS_Y] = 88 },
+       .id      = {
+               .bustype = BUS_ISA,
+               .vendor  = 0x0005,
+               .product = 0x0001,
+               .version = 0x0100,
+       },
+};
+
+int __init mk712_init(void)
+{
+
+       if(!request_region(mk712_io, 8, "mk712"))
+       {
+               printk(KERN_WARNING "mk712: unable to get IO region\n");
+               return -ENODEV;
+       }
+
+       outb(0, mk712_io + MK712_CONTROL);
+
+       if ((inw(mk712_io + MK712_X) & 0xf000) ||       /* Sanity check */
+           (inw(mk712_io + MK712_Y) & 0xf000) ||
+           (inw(mk712_io + MK712_STATUS) & 0xf333)) {
+               printk(KERN_WARNING "mk712: device not present\n");
+               release_region(mk712_io, 8);
+               return -ENODEV;
+       }
+
+       if(request_irq(mk712_irq, mk712_interrupt, 0, "mk712", &mk712_dev))
+       {
+               printk(KERN_WARNING "mk712: unable to get IRQ\n");
+               release_region(mk712_io, 8);
+               return -EBUSY;
+       }
+
+       input_register_device(&mk712_dev);
+
+       printk(KERN_INFO "input: ICS MicroClock MK712 TouchScreen at %#x irq %d\n", mk712_io, mk712_irq);
+
+       return 0;
+}
+
+static void __exit mk712_exit(void)
+{
+       input_unregister_device(&mk712_dev);
+       free_irq(mk712_irq, &mk712_dev);
+       release_region(mk712_io, 8);
+}
+
+module_init(mk712_init);
+module_exit(mk712_exit);
diff --git a/drivers/input/touchscreen/mtouch.c b/drivers/input/touchscreen/mtouch.c
new file mode 100644 (file)
index 0000000..aa8ee78
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * MicroTouch (3M) serial touchscreen driver
+ *
+ * Copyright (c) 2004 Vojtech Pavlik
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * 2005/02/19 Dan Streetman <ddstreet@ieee.org>
+ *   Copied elo.c and edited for MicroTouch protocol
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC    "MicroTouch serial touchscreen driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define MTOUCH_FORMAT_TABLET_STATUS_BIT 0x80
+#define MTOUCH_FORMAT_TABLET_TOUCH_BIT 0x40
+#define MTOUCH_FORMAT_TABLET_LENGTH 5
+#define MTOUCH_RESPONSE_BEGIN_BYTE 0x01
+#define MTOUCH_RESPONSE_END_BYTE 0x0d
+
+/* todo: check specs for max length of all responses */
+#define MTOUCH_MAX_LENGTH 16
+
+#define MTOUCH_MIN_XC 0
+#define MTOUCH_MAX_XC 0x3fff
+#define MTOUCH_MIN_YC 0
+#define MTOUCH_MAX_YC 0x3fff
+
+#define MTOUCH_GET_XC(data) (((data[2])<<7) | data[1])
+#define MTOUCH_GET_YC(data) (((data[4])<<7) | data[3])
+#define MTOUCH_GET_TOUCHED(data) (MTOUCH_FORMAT_TABLET_TOUCH_BIT & data[0])
+
+static char *mtouch_name = "MicroTouch Serial TouchScreen";
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct mtouch {
+       struct input_dev dev;
+       struct serio *serio;
+       int idx;
+       unsigned char data[MTOUCH_MAX_LENGTH];
+       char phys[32];
+};
+
+static void mtouch_process_format_tablet(struct mtouch *mtouch, struct pt_regs *regs)
+{
+       struct input_dev *dev = &mtouch->dev;
+
+       if (MTOUCH_FORMAT_TABLET_LENGTH == ++mtouch->idx) {
+               input_regs(dev, regs);
+               input_report_abs(dev, ABS_X, MTOUCH_GET_XC(mtouch->data));
+               input_report_abs(dev, ABS_Y, MTOUCH_MAX_YC - MTOUCH_GET_YC(mtouch->data));
+               input_report_key(dev, BTN_TOUCH, MTOUCH_GET_TOUCHED(mtouch->data));
+               input_sync(dev);
+
+               mtouch->idx = 0;
+       }
+}
+
+static void mtouch_process_response(struct mtouch *mtouch, struct pt_regs *regs)
+{
+       if (MTOUCH_RESPONSE_END_BYTE == mtouch->data[mtouch->idx++]) {
+               /* FIXME - process response */
+               mtouch->idx = 0;
+       } else if (MTOUCH_MAX_LENGTH == mtouch->idx) {
+               printk(KERN_ERR "mtouch.c: too many response bytes\n");
+               mtouch->idx = 0;
+       }
+}
+
+static irqreturn_t mtouch_interrupt(struct serio *serio,
+               unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+       struct mtouch* mtouch = serio_get_drvdata(serio);
+
+       mtouch->data[mtouch->idx] = data;
+
+       if (MTOUCH_FORMAT_TABLET_STATUS_BIT & mtouch->data[0])
+               mtouch_process_format_tablet(mtouch, regs);
+       else if (MTOUCH_RESPONSE_BEGIN_BYTE == mtouch->data[0])
+               mtouch_process_response(mtouch, regs);
+       else
+               printk(KERN_DEBUG "mtouch.c: unknown/unsynchronized data from device, byte %x\n",mtouch->data[0]);
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * mtouch_disconnect() is the opposite of mtouch_connect()
+ */
+
+static void mtouch_disconnect(struct serio *serio)
+{
+       struct mtouch* mtouch = serio_get_drvdata(serio);
+
+       input_unregister_device(&mtouch->dev);
+       serio_close(serio);
+       serio_set_drvdata(serio, NULL);
+       kfree(mtouch);
+}
+
+/*
+ * mtouch_connect() is the routine that is called when someone adds a
+ * new serio device that supports MicroTouch (Format Tablet) protocol and registers it as
+ * an input device.
+ */
+
+static int mtouch_connect(struct serio *serio, struct serio_driver *drv)
+{
+       struct mtouch *mtouch;
+       int err;
+
+       if (!(mtouch = kmalloc(sizeof(*mtouch), GFP_KERNEL)))
+               return -ENOMEM;
+
+       memset(mtouch, 0, sizeof(*mtouch));
+
+       init_input_dev(&mtouch->dev);
+       mtouch->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+       mtouch->dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+
+       input_set_abs_params(&mtouch->dev, ABS_X, MTOUCH_MIN_XC, MTOUCH_MAX_XC, 0, 0);
+       input_set_abs_params(&mtouch->dev, ABS_Y, MTOUCH_MIN_YC, MTOUCH_MAX_YC, 0, 0);
+
+       mtouch->serio = serio;
+
+       sprintf(mtouch->phys, "%s/input0", serio->phys);
+
+       mtouch->dev.private = mtouch;
+       mtouch->dev.name = mtouch_name;
+       mtouch->dev.phys = mtouch->phys;
+       mtouch->dev.id.bustype = BUS_RS232;
+       mtouch->dev.id.vendor = SERIO_MICROTOUCH;
+       mtouch->dev.id.product = 0;
+       mtouch->dev.id.version = 0x0100;
+
+       serio_set_drvdata(serio, mtouch);
+
+       err = serio_open(serio, drv);
+       if (err) {
+               serio_set_drvdata(serio, NULL);
+               kfree(mtouch);
+               return err;
+       }
+
+       input_register_device(&mtouch->dev);
+
+       printk(KERN_INFO "input: %s on %s\n", mtouch->dev.name, serio->phys);
+
+       return 0;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id mtouch_serio_ids[] = {
+       {
+               .type   = SERIO_RS232,
+               .proto  = SERIO_MICROTOUCH,
+               .id     = SERIO_ANY,
+               .extra  = SERIO_ANY,
+       },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, mtouch_serio_ids);
+
+static struct serio_driver mtouch_drv = {
+       .driver         = {
+               .name   = "mtouch",
+       },
+       .description    = DRIVER_DESC,
+       .id_table       = mtouch_serio_ids,
+       .interrupt      = mtouch_interrupt,
+       .connect        = mtouch_connect,
+       .disconnect     = mtouch_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init mtouch_init(void)
+{
+       serio_register_driver(&mtouch_drv);
+       return 0;
+}
+
+static void __exit mtouch_exit(void)
+{
+       serio_unregister_driver(&mtouch_drv);
+}
+
+module_init(mtouch_init);
+module_exit(mtouch_exit);
diff --git a/drivers/isdn/hisax/hfc4s8s_l1.c b/drivers/isdn/hisax/hfc4s8s_l1.c
new file mode 100644 (file)
index 0000000..ba1d028
--- /dev/null
@@ -0,0 +1,1715 @@
+/*************************************************************************/
+/* $Id: hfc4s8s_l1.c,v 1.10 2005/02/09 16:31:09 martinb1 Exp $           */
+/* HFC-4S/8S low layer interface for Cologne Chip HFC-4S/8S isdn chips   */
+/* The low layer (L1) is implemented as a loadable module for usage with */
+/* the HiSax isdn driver for passive cards.                              */
+/*                                                                       */
+/* Author: Werner Cornelius                                              */
+/* (C) 2003 Cornelius Consult (werner@cornelius-consult.de)              */
+/*                                                                       */
+/* Driver maintained by Cologne Chip                                     */
+/*   - Martin Bachem, support@colognechip.com                            */
+/*                                                                       */
+/* This driver only works with chip revisions >= 1, older revision 0     */
+/* engineering samples (only first manufacturer sample cards) will not   */
+/* work and are rejected by the driver.                                  */
+/*                                                                       */
+/* This file distributed under the GNU GPL.                              */
+/*                                                                       */
+/* See Version History at the end of this file                           */
+/*                                                                       */
+/*************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/skbuff.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "hisax_if.h"
+#include "hfc4s8s_l1.h"
+
+static const char hfc4s8s_rev[] = "Revision: 1.10";
+
+/***************************************************************/
+/* adjustable transparent mode fifo threshold                  */
+/* The value defines the used fifo threshold with the equation */
+/*                                                             */
+/* notify number of bytes = 2 * 2 ^ TRANS_FIFO_THRES           */
+/*                                                             */
+/* The default value is 5 which results in a buffer size of 64 */
+/* and an interrupt rate of 8ms.                               */
+/* The maximum value is 7 due to fifo size restrictions.       */
+/* Values below 3-4 are not recommended due to high interrupt  */
+/* load of the processor. For non critical applications the    */
+/* value should be raised to 7 to reduce any interrupt overhead*/
+/***************************************************************/
+#define TRANS_FIFO_THRES 5
+
+/*************/
+/* constants */
+/*************/
+#define CLOCKMODE_0     0      /* ext. 24.576 MhZ clk freq, int. single clock mode */
+#define CLOCKMODE_1     1      /* ext. 49.576 MhZ clk freq, int. single clock mode */
+#define CHIP_ID_SHIFT   4
+#define HFC_MAX_ST 8
+#define MAX_D_FRAME_SIZE 270
+#define MAX_B_FRAME_SIZE 1536
+#define TRANS_TIMER_MODE (TRANS_FIFO_THRES & 0xf)
+#define TRANS_FIFO_BYTES (2 << TRANS_FIFO_THRES)
+#define MAX_F_CNT 0x0f
+
+#define CLKDEL_NT 0x6c
+#define CLKDEL_TE 0xf
+#define CTRL0_NT  4
+#define CTRL0_TE  0
+
+#define L1_TIMER_T4 2          /* minimum in jiffies */
+#define L1_TIMER_T3 (7 * HZ)   /* activation timeout */
+#define L1_TIMER_T1 ((120 * HZ) / 1000)        /* NT mode deactivation timeout */
+
+
+/******************/
+/* types and vars */
+/******************/
+static int card_cnt;
+
+/* private driver_data */
+typedef struct {
+       int chip_id;
+       int clock_mode;
+       int max_st_ports;
+       char *device_name;
+} hfc4s8s_param;
+
+static struct pci_device_id hfc4s8s_ids[] = {
+       {.vendor = PCI_VENDOR_ID_CCD,
+        .device = PCI_DEVICE_ID_4S,
+        .subvendor = 0x1397,
+        .subdevice = 0x08b4,
+        .driver_data =
+        (unsigned long) &((hfc4s8s_param) {CHIP_ID_4S, CLOCKMODE_0, 4,
+                                           "HFC-4S Evaluation Board"}),
+        },
+       {.vendor = PCI_VENDOR_ID_CCD,
+        .device = PCI_DEVICE_ID_8S,
+        .subvendor = 0x1397,
+        .subdevice = 0x16b8,
+        .driver_data =
+        (unsigned long) &((hfc4s8s_param) {CHIP_ID_8S, CLOCKMODE_0, 8,
+                                           "HFC-8S Evaluation Board"}),
+        },
+       {.vendor = PCI_VENDOR_ID_CCD,
+        .device = PCI_DEVICE_ID_4S,
+        .subvendor = 0x1397,
+        .subdevice = 0xb520,
+        .driver_data =
+        (unsigned long) &((hfc4s8s_param) {CHIP_ID_4S, CLOCKMODE_1, 4,
+                                           "IOB4ST"}),
+        },
+       {.vendor = PCI_VENDOR_ID_CCD,
+        .device = PCI_DEVICE_ID_8S,
+        .subvendor = 0x1397,
+        .subdevice = 0xb522,
+        .driver_data =
+        (unsigned long) &((hfc4s8s_param) {CHIP_ID_8S, CLOCKMODE_1, 8,
+                                           "IOB8ST"}),
+        },
+       {}
+};
+
+MODULE_DEVICE_TABLE(pci, hfc4s8s_ids);
+
+MODULE_AUTHOR("Werner Cornelius, werner@cornelius-consult.de");
+MODULE_DESCRIPTION("ISDN layer 1 for Cologne Chip HFC-4S/8S chips");
+MODULE_LICENSE("GPL");
+
+/***********/
+/* layer 1 */
+/***********/
+struct hfc4s8s_btype {
+       spinlock_t lock;
+       struct hisax_b_if b_if;
+       struct hfc4s8s_l1 *l1p;
+       struct sk_buff_head tx_queue;
+       struct sk_buff *tx_skb;
+       struct sk_buff *rx_skb;
+       __u8 *rx_ptr;
+       int tx_cnt;
+       int bchan;
+       int mode;
+};
+
+struct _hfc4s8s_hw;
+
+struct hfc4s8s_l1 {
+       spinlock_t lock;
+       struct _hfc4s8s_hw *hw; /* pointer to hardware area */
+       int l1_state;           /* actual l1 state */
+       struct timer_list l1_timer;     /* layer 1 timer structure */
+       int nt_mode;            /* set to nt mode */
+       int st_num;             /* own index */
+       int enabled;            /* interface is enabled */
+       struct sk_buff_head d_tx_queue; /* send queue */
+       int tx_cnt;             /* bytes to send */
+       struct hisax_d_if d_if; /* D-channel interface */
+       struct hfc4s8s_btype b_ch[2];   /* B-channel data */
+       struct hisax_b_if *b_table[2];
+};
+
+/**********************/
+/* hardware structure */
+/**********************/
+typedef struct _hfc4s8s_hw {
+       spinlock_t lock;
+
+       int cardnum;
+       int ifnum;
+       int iobase;
+       int nt_mode;
+       u_char *membase;
+       u_char *hw_membase;
+       void *pdev;
+       int max_fifo;
+       hfc4s8s_param driver_data;
+       int irq;
+       int fifo_sched_cnt;
+       struct work_struct tqueue;
+       struct hfc4s8s_l1 l1[HFC_MAX_ST];
+       char card_name[60];
+       struct {
+               u_char r_irq_ctrl;
+               u_char r_ctrl0;
+               volatile u_char r_irq_statech;  /* active isdn l1 status */
+               u_char r_irqmsk_statchg;        /* enabled isdn status ints */
+               u_char r_irq_fifo_blx[8];       /* fifo status registers */
+               u_char fifo_rx_trans_enables[8];        /* mask for enabled transparent rx fifos */
+               u_char fifo_slow_timer_service[8];      /* mask for fifos needing slower timer service */
+               volatile u_char r_irq_oview;    /* contents of overview register */
+               volatile u_char timer_irq;
+               int timer_usg_cnt;      /* number of channels using timer */
+       } mr;
+} hfc4s8s_hw;
+
+
+
+/***************************/
+/* inline function defines */
+/***************************/
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM     /* inline functions mempry mapped */
+
+/* memory write and dummy IO read to avoid PCI byte merge problems */
+#define Write_hfc8(a,b,c) {(*((volatile u_char *)(a->membase+b)) = c); inb(a->iobase+4);}
+/* memory write without dummy IO access for fifo data access */
+#define fWrite_hfc8(a,b,c) (*((volatile u_char *)(a->membase+b)) = c)
+#define Read_hfc8(a,b) (*((volatile u_char *)(a->membase+b)))
+#define Write_hfc16(a,b,c) (*((volatile unsigned short *)(a->membase+b)) = c)
+#define Read_hfc16(a,b) (*((volatile unsigned short *)(a->membase+b)))
+#define Write_hfc32(a,b,c) (*((volatile unsigned long *)(a->membase+b)) = c)
+#define Read_hfc32(a,b) (*((volatile unsigned long *)(a->membase+b)))
+#define wait_busy(a) {while ((Read_hfc8(a, R_STATUS) & M_BUSY));}
+#define PCI_ENA_MEMIO  0x03
+
+#else
+
+/* inline functions io mapped */
+static inline void
+SetRegAddr(hfc4s8s_hw * a, u_char b)
+{
+       outb(b, (a->iobase) + 4);
+}
+
+static inline u_char
+GetRegAddr(hfc4s8s_hw * a)
+{
+       return (inb((volatile u_int) (a->iobase + 4)));
+}
+
+
+static inline void
+Write_hfc8(hfc4s8s_hw * a, u_char b, u_char c)
+{
+       SetRegAddr(a, b);
+       outb(c, a->iobase);
+}
+
+static inline void
+fWrite_hfc8(hfc4s8s_hw * a, u_char c)
+{
+       outb(c, a->iobase);
+}
+
+static inline void
+Write_hfc16(hfc4s8s_hw * a, u_char b, u_short c)
+{
+       SetRegAddr(a, b);
+       outw(c, a->iobase);
+}
+
+static inline void
+Write_hfc32(hfc4s8s_hw * a, u_char b, u_long c)
+{
+       SetRegAddr(a, b);
+       outl(c, a->iobase);
+}
+
+static inline void
+fWrite_hfc32(hfc4s8s_hw * a, u_long c)
+{
+       outl(c, a->iobase);
+}
+
+static inline u_char
+Read_hfc8(hfc4s8s_hw * a, u_char b)
+{
+       SetRegAddr(a, b);
+       return (inb((volatile u_int) a->iobase));
+}
+
+static inline u_char
+fRead_hfc8(hfc4s8s_hw * a)
+{
+       return (inb((volatile u_int) a->iobase));
+}
+
+
+static inline u_short
+Read_hfc16(hfc4s8s_hw * a, u_char b)
+{
+       SetRegAddr(a, b);
+       return (inw((volatile u_int) a->iobase));
+}
+
+static inline u_long
+Read_hfc32(hfc4s8s_hw * a, u_char b)
+{
+       SetRegAddr(a, b);
+       return (inl((volatile u_int) a->iobase));
+}
+
+static inline u_long
+fRead_hfc32(hfc4s8s_hw * a)
+{
+       return (inl((volatile u_int) a->iobase));
+}
+
+static inline void
+wait_busy(hfc4s8s_hw * a)
+{
+       SetRegAddr(a, R_STATUS);
+       while (inb((volatile u_int) a->iobase) & M_BUSY);
+}
+
+#define PCI_ENA_REGIO  0x01
+
+#endif                         /* CONFIG_HISAX_HFC4S8S_PCIMEM */
+
+/******************************************************/
+/* function to read critical counter registers that   */
+/* may be udpated by the chip during read             */
+/******************************************************/
+static volatile u_char
+Read_hfc8_stable(hfc4s8s_hw * hw, int reg)
+{
+       u_char ref8;
+       u_char in8;
+       ref8 = Read_hfc8(hw, reg);
+       while (((in8 = Read_hfc8(hw, reg)) != ref8)) {
+               ref8 = in8;
+       }
+       return in8;
+}
+
+static volatile int
+Read_hfc16_stable(hfc4s8s_hw * hw, int reg)
+{
+       int ref16;
+       int in16;
+
+       ref16 = Read_hfc16(hw, reg);
+       while (((in16 = Read_hfc16(hw, reg)) != ref16)) {
+               ref16 = in16;
+       }
+       return in16;
+}
+
+/*****************************/
+/* D-channel call from HiSax */
+/*****************************/
+static void
+dch_l2l1(struct hisax_d_if *iface, int pr, void *arg)
+{
+       struct hfc4s8s_l1 *l1 = iface->ifc.priv;
+       struct sk_buff *skb = (struct sk_buff *) arg;
+       u_long flags;
+
+       switch (pr) {
+
+               case (PH_DATA | REQUEST):
+                       if (!l1->enabled) {
+                               dev_kfree_skb(skb);
+                               break;
+                       }
+                       spin_lock_irqsave(&l1->lock, flags);
+                       skb_queue_tail(&l1->d_tx_queue, skb);
+                       if ((skb_queue_len(&l1->d_tx_queue) == 1) &&
+                           (l1->tx_cnt <= 0)) {
+                               l1->hw->mr.r_irq_fifo_blx[l1->st_num] |=
+                                   0x10;
+                               spin_unlock_irqrestore(&l1->lock, flags);
+                               schedule_work(&l1->hw->tqueue);
+                       } else
+                               spin_unlock_irqrestore(&l1->lock, flags);
+                       break;
+
+               case (PH_ACTIVATE | REQUEST):
+                       if (!l1->enabled)
+                               break;
+                       if (!l1->nt_mode) {
+                               if (l1->l1_state < 6) {
+                                       spin_lock_irqsave(&l1->lock,
+                                                         flags);
+
+                                       Write_hfc8(l1->hw, R_ST_SEL,
+                                                  l1->st_num);
+                                       Write_hfc8(l1->hw, A_ST_WR_STA,
+                                                  0x60);
+                                       mod_timer(&l1->l1_timer,
+                                                 jiffies + L1_TIMER_T3);
+                                       spin_unlock_irqrestore(&l1->lock,
+                                                              flags);
+                               } else if (l1->l1_state == 7)
+                                       l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+                                                         PH_ACTIVATE |
+                                                         INDICATION,
+                                                         NULL);
+                       } else {
+                               if (l1->l1_state != 3) {
+                                       spin_lock_irqsave(&l1->lock,
+                                                         flags);
+                                       Write_hfc8(l1->hw, R_ST_SEL,
+                                                  l1->st_num);
+                                       Write_hfc8(l1->hw, A_ST_WR_STA,
+                                                  0x60);
+                                       spin_unlock_irqrestore(&l1->lock,
+                                                              flags);
+                               } else if (l1->l1_state == 3)
+                                       l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+                                                         PH_ACTIVATE |
+                                                         INDICATION,
+                                                         NULL);
+                       }
+                       break;
+
+               default:
+                       printk(KERN_INFO
+                              "HFC-4S/8S: Unknown D-chan cmd 0x%x received, ignored\n",
+                              pr);
+                       break;
+       }
+       if (!l1->enabled)
+               l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+                                 PH_DEACTIVATE | INDICATION, NULL);
+}                              /* dch_l2l1 */
+
+/*****************************/
+/* B-channel call from HiSax */
+/*****************************/
+static void
+bch_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+       struct hfc4s8s_btype *bch = ifc->priv;
+       struct hfc4s8s_l1 *l1 = bch->l1p;
+       struct sk_buff *skb = (struct sk_buff *) arg;
+       int mode = (int) arg;
+       u_long flags;
+
+       switch (pr) {
+
+               case (PH_DATA | REQUEST):
+                       if (!l1->enabled || (bch->mode == L1_MODE_NULL)) {
+                               dev_kfree_skb(skb);
+                               break;
+                       }
+                       spin_lock_irqsave(&l1->lock, flags);
+                       skb_queue_tail(&bch->tx_queue, skb);
+                       if (!bch->tx_skb && (bch->tx_cnt <= 0)) {
+                               l1->hw->mr.r_irq_fifo_blx[l1->st_num] |=
+                                   ((bch->bchan == 1) ? 1 : 4);
+                               spin_unlock_irqrestore(&l1->lock, flags);
+                               schedule_work(&l1->hw->tqueue);
+                       } else
+                               spin_unlock_irqrestore(&l1->lock, flags);
+                       break;
+
+               case (PH_ACTIVATE | REQUEST):
+               case (PH_DEACTIVATE | REQUEST):
+                       if (!l1->enabled)
+                               break;
+                       if (pr == (PH_DEACTIVATE | REQUEST))
+                               mode = L1_MODE_NULL;
+
+                       switch (mode) {
+                               case L1_MODE_HDLC:
+                                       spin_lock_irqsave(&l1->lock,
+                                                         flags);
+                                       l1->hw->mr.timer_usg_cnt++;
+                                       l1->hw->mr.
+                                           fifo_slow_timer_service[l1->
+                                                                   st_num]
+                                           |=
+                                           ((bch->bchan ==
+                                             1) ? 0x2 : 0x8);
+                                       Write_hfc8(l1->hw, R_FIFO,
+                                                  (l1->st_num * 8 +
+                                                   ((bch->bchan ==
+                                                     1) ? 0 : 2)));
+                                       wait_busy(l1->hw);
+                                       Write_hfc8(l1->hw, A_CON_HDLC, 0xc);    /* HDLC mode, flag fill, connect ST */
+                                       Write_hfc8(l1->hw, A_SUBCH_CFG, 0);     /* 8 bits */
+                                       Write_hfc8(l1->hw, A_IRQ_MSK, 1);       /* enable TX interrupts for hdlc */
+                                       Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);  /* reset fifo */
+                                       wait_busy(l1->hw);
+
+                                       Write_hfc8(l1->hw, R_FIFO,
+                                                  (l1->st_num * 8 +
+                                                   ((bch->bchan ==
+                                                     1) ? 1 : 3)));
+                                       wait_busy(l1->hw);
+                                       Write_hfc8(l1->hw, A_CON_HDLC, 0xc);    /* HDLC mode, flag fill, connect ST */
+                                       Write_hfc8(l1->hw, A_SUBCH_CFG, 0);     /* 8 bits */
+                                       Write_hfc8(l1->hw, A_IRQ_MSK, 1);       /* enable RX interrupts for hdlc */
+                                       Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);  /* reset fifo */
+
+                                       Write_hfc8(l1->hw, R_ST_SEL,
+                                                  l1->st_num);
+                                       l1->hw->mr.r_ctrl0 |=
+                                           (bch->bchan & 3);
+                                       Write_hfc8(l1->hw, A_ST_CTRL0,
+                                                  l1->hw->mr.r_ctrl0);
+                                       bch->mode = L1_MODE_HDLC;
+                                       spin_unlock_irqrestore(&l1->lock,
+                                                              flags);
+
+                                       bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+                                                          PH_ACTIVATE |
+                                                          INDICATION,
+                                                          NULL);
+                                       break;
+
+                               case L1_MODE_TRANS:
+                                       spin_lock_irqsave(&l1->lock,
+                                                         flags);
+                                       l1->hw->mr.
+                                           fifo_rx_trans_enables[l1->
+                                                                 st_num]
+                                           |=
+                                           ((bch->bchan ==
+                                             1) ? 0x2 : 0x8);
+                                       l1->hw->mr.timer_usg_cnt++;
+                                       Write_hfc8(l1->hw, R_FIFO,
+                                                  (l1->st_num * 8 +
+                                                   ((bch->bchan ==
+                                                     1) ? 0 : 2)));
+                                       wait_busy(l1->hw);
+                                       Write_hfc8(l1->hw, A_CON_HDLC, 0xf);    /* Transparent mode, 1 fill, connect ST */
+                                       Write_hfc8(l1->hw, A_SUBCH_CFG, 0);     /* 8 bits */
+                                       Write_hfc8(l1->hw, A_IRQ_MSK, 0);       /* disable TX interrupts */
+                                       Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);  /* reset fifo */
+                                       wait_busy(l1->hw);
+
+                                       Write_hfc8(l1->hw, R_FIFO,
+                                                  (l1->st_num * 8 +
+                                                   ((bch->bchan ==
+                                                     1) ? 1 : 3)));
+                                       wait_busy(l1->hw);
+                                       Write_hfc8(l1->hw, A_CON_HDLC, 0xf);    /* Transparent mode, 1 fill, connect ST */
+                                       Write_hfc8(l1->hw, A_SUBCH_CFG, 0);     /* 8 bits */
+                                       Write_hfc8(l1->hw, A_IRQ_MSK, 0);       /* disable RX interrupts */
+                                       Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);  /* reset fifo */
+
+                                       Write_hfc8(l1->hw, R_ST_SEL,
+                                                  l1->st_num);
+                                       l1->hw->mr.r_ctrl0 |=
+                                           (bch->bchan & 3);
+                                       Write_hfc8(l1->hw, A_ST_CTRL0,
+                                                  l1->hw->mr.r_ctrl0);
+                                       bch->mode = L1_MODE_TRANS;
+                                       spin_unlock_irqrestore(&l1->lock,
+                                                              flags);
+
+                                       bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+                                                          PH_ACTIVATE |
+                                                          INDICATION,
+                                                          NULL);
+                                       break;
+
+                               default:
+                                       if (bch->mode == L1_MODE_NULL)
+                                               break;
+                                       spin_lock_irqsave(&l1->lock,
+                                                         flags);
+                                       l1->hw->mr.
+                                           fifo_slow_timer_service[l1->
+                                                                   st_num]
+                                           &=
+                                           ~((bch->bchan ==
+                                              1) ? 0x3 : 0xc);
+                                       l1->hw->mr.
+                                           fifo_rx_trans_enables[l1->
+                                                                 st_num]
+                                           &=
+                                           ~((bch->bchan ==
+                                              1) ? 0x3 : 0xc);
+                                       l1->hw->mr.timer_usg_cnt--;
+                                       Write_hfc8(l1->hw, R_FIFO,
+                                                  (l1->st_num * 8 +
+                                                   ((bch->bchan ==
+                                                     1) ? 0 : 2)));
+                                       wait_busy(l1->hw);
+                                       Write_hfc8(l1->hw, A_IRQ_MSK, 0);       /* disable TX interrupts */
+                                       wait_busy(l1->hw);
+                                       Write_hfc8(l1->hw, R_FIFO,
+                                                  (l1->st_num * 8 +
+                                                   ((bch->bchan ==
+                                                     1) ? 1 : 3)));
+                                       wait_busy(l1->hw);
+                                       Write_hfc8(l1->hw, A_IRQ_MSK, 0);       /* disable RX interrupts */
+                                       Write_hfc8(l1->hw, R_ST_SEL,
+                                                  l1->st_num);
+                                       l1->hw->mr.r_ctrl0 &=
+                                           ~(bch->bchan & 3);
+                                       Write_hfc8(l1->hw, A_ST_CTRL0,
+                                                  l1->hw->mr.r_ctrl0);
+                                       spin_unlock_irqrestore(&l1->lock,
+                                                              flags);
+
+                                       bch->mode = L1_MODE_NULL;
+                                       bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+                                                          PH_DEACTIVATE |
+                                                          INDICATION,
+                                                          NULL);
+                                       if (bch->tx_skb) {
+                                               dev_kfree_skb(bch->tx_skb);
+                                               bch->tx_skb = NULL;
+                                       }
+                                       if (bch->rx_skb) {
+                                               dev_kfree_skb(bch->rx_skb);
+                                               bch->rx_skb = NULL;
+                                       }
+                                       skb_queue_purge(&bch->tx_queue);
+                                       bch->tx_cnt = 0;
+                                       bch->rx_ptr = NULL;
+                                       break;
+                       }
+
+                       /* timer is only used when at least one b channel */
+                       /* is set up to transparent mode */
+                       if (l1->hw->mr.timer_usg_cnt) {
+                               Write_hfc8(l1->hw, R_IRQMSK_MISC,
+                                          M_TI_IRQMSK);
+                       } else {
+                               Write_hfc8(l1->hw, R_IRQMSK_MISC, 0);
+                       }
+
+                       break;
+
+               default:
+                       printk(KERN_INFO
+                              "HFC-4S/8S: Unknown B-chan cmd 0x%x received, ignored\n",
+                              pr);
+                       break;
+       }
+       if (!l1->enabled)
+               bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+                                  PH_DEACTIVATE | INDICATION, NULL);
+}                              /* bch_l2l1 */
+
+/**************************/
+/* layer 1 timer function */
+/**************************/
+static void
+hfc_l1_timer(struct hfc4s8s_l1 *l1)
+{
+       u_long flags;
+
+       if (!l1->enabled)
+               return;
+
+       spin_lock_irqsave(&l1->lock, flags);
+       if (l1->nt_mode) {
+               l1->l1_state = 1;
+               Write_hfc8(l1->hw, R_ST_SEL, l1->st_num);
+               Write_hfc8(l1->hw, A_ST_WR_STA, 0x11);
+               spin_unlock_irqrestore(&l1->lock, flags);
+               l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+                                 PH_DEACTIVATE | INDICATION, NULL);
+               spin_lock_irqsave(&l1->lock, flags);
+               l1->l1_state = 1;
+               Write_hfc8(l1->hw, A_ST_WR_STA, 0x1);
+               spin_unlock_irqrestore(&l1->lock, flags);
+       } else {
+               /* activation timed out */
+               Write_hfc8(l1->hw, R_ST_SEL, l1->st_num);
+               Write_hfc8(l1->hw, A_ST_WR_STA, 0x13);
+               spin_unlock_irqrestore(&l1->lock, flags);
+               l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+                                 PH_DEACTIVATE | INDICATION, NULL);
+               spin_lock_irqsave(&l1->lock, flags);
+               Write_hfc8(l1->hw, R_ST_SEL, l1->st_num);
+               Write_hfc8(l1->hw, A_ST_WR_STA, 0x3);
+               spin_unlock_irqrestore(&l1->lock, flags);
+       }
+}                              /* hfc_l1_timer */
+
+/****************************************/
+/* a complete D-frame has been received */
+/****************************************/
+static void
+rx_d_frame(struct hfc4s8s_l1 *l1p, int ech)
+{
+       int z1, z2;
+       u_char f1, f2, df;
+       struct sk_buff *skb;
+       u_char *cp;
+
+
+       if (!l1p->enabled)
+               return;
+       do {
+               /* E/D RX fifo */
+               Write_hfc8(l1p->hw, R_FIFO,
+                          (l1p->st_num * 8 + ((ech) ? 7 : 5)));
+               wait_busy(l1p->hw);
+
+               f1 = Read_hfc8_stable(l1p->hw, A_F1);
+               f2 = Read_hfc8(l1p->hw, A_F2);
+               df = f1 - f2;
+               if ((f1 - f2) < 0)
+                       df = f1 - f2 + MAX_F_CNT + 1;
+
+
+               if (!df) {
+                       return; /* no complete frame in fifo */
+               }
+
+               z1 = Read_hfc16_stable(l1p->hw, A_Z1);
+               z2 = Read_hfc16(l1p->hw, A_Z2);
+
+               z1 = z1 - z2 + 1;
+               if (z1 < 0)
+                       z1 += 384;
+
+               if (!(skb = dev_alloc_skb(MAX_D_FRAME_SIZE))) {
+                       printk(KERN_INFO
+                              "HFC-4S/8S: Could not allocate D/E "
+                              "channel receive buffer");
+                       Write_hfc8(l1p->hw, A_INC_RES_FIFO, 2);
+                       wait_busy(l1p->hw);
+                       return;
+               }
+
+               if (((z1 < 4) || (z1 > MAX_D_FRAME_SIZE))) {
+                       if (skb)
+                               dev_kfree_skb(skb);
+                       /* remove errornous D frame */
+                       if (df == 1) {
+                               /* reset fifo */
+                               Write_hfc8(l1p->hw, A_INC_RES_FIFO, 2);
+                               wait_busy(l1p->hw);
+                               return;
+                       } else {
+                               /* read errornous D frame */
+
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+                               SetRegAddr(l1p->hw, A_FIFO_DATA0);
+#endif
+
+                               while (z1 >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+                                       Read_hfc32(l1p->hw, A_FIFO_DATA0);
+#else
+                                       fRead_hfc32(l1p->hw);
+#endif
+                                       z1 -= 4;
+                               }
+
+                               while (z1--)
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+                                       Read_hfc8(l1p->hw, A_FIFO_DATA0);
+#else
+                                       fRead_hfc8(l1p->hw);
+#endif
+
+                               Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1);
+                               wait_busy(l1p->hw);
+                               return;
+                       }
+               }
+
+               cp = skb->data;
+
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+               SetRegAddr(l1p->hw, A_FIFO_DATA0);
+#endif
+
+               while (z1 >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+                       *((unsigned long *) cp) =
+                           Read_hfc32(l1p->hw, A_FIFO_DATA0);
+#else
+                       *((unsigned long *) cp) = fRead_hfc32(l1p->hw);
+#endif
+                       cp += 4;
+                       z1 -= 4;
+               }
+
+               while (z1--)
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+                       *cp++ = Read_hfc8(l1p->hw, A_FIFO_DATA0);
+#else
+                       *cp++ = fRead_hfc8(l1p->hw);
+#endif
+
+               Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1); /* increment f counter */
+               wait_busy(l1p->hw);
+
+               if (*(--cp)) {
+                       dev_kfree_skb(skb);
+               } else {
+                       skb->len = (cp - skb->data) - 2;
+                       if (ech)
+                               l1p->d_if.ifc.l1l2(&l1p->d_if.ifc,
+                                                  PH_DATA_E | INDICATION,
+                                                  skb);
+                       else
+                               l1p->d_if.ifc.l1l2(&l1p->d_if.ifc,
+                                                  PH_DATA | INDICATION,
+                                                  skb);
+               }
+       } while (1);
+}                              /* rx_d_frame */
+
+/*************************************************************/
+/* a B-frame has been received (perhaps not fully completed) */
+/*************************************************************/
+static void
+rx_b_frame(struct hfc4s8s_btype *bch)
+{
+       int z1, z2, hdlc_complete;
+       u_char f1, f2;
+       struct hfc4s8s_l1 *l1 = bch->l1p;
+       struct sk_buff *skb;
+
+       if (!l1->enabled || (bch->mode == L1_MODE_NULL))
+               return;
+
+       do {
+               /* RX Fifo */
+               Write_hfc8(l1->hw, R_FIFO,
+                          (l1->st_num * 8 + ((bch->bchan == 1) ? 1 : 3)));
+               wait_busy(l1->hw);
+
+               if (bch->mode == L1_MODE_HDLC) {
+                       f1 = Read_hfc8_stable(l1->hw, A_F1);
+                       f2 = Read_hfc8(l1->hw, A_F2);
+                       hdlc_complete = ((f1 ^ f2) & MAX_F_CNT);
+               } else
+                       hdlc_complete = 0;
+               z1 = Read_hfc16_stable(l1->hw, A_Z1);
+               z2 = Read_hfc16(l1->hw, A_Z2);
+               z1 = (z1 - z2);
+               if (hdlc_complete)
+                       z1++;
+               if (z1 < 0)
+                       z1 += 384;
+
+               if (!z1)
+                       break;
+
+               if (!(skb = bch->rx_skb)) {
+                       if (!
+                           (skb =
+                            dev_alloc_skb((bch->mode ==
+                                           L1_MODE_TRANS) ? z1
+                                          : (MAX_B_FRAME_SIZE + 3)))) {
+                               printk(KERN_ERR
+                                      "HFC-4S/8S: Could not allocate B "
+                                      "channel receive buffer");
+                               return;
+                       }
+                       bch->rx_ptr = skb->data;
+                       bch->rx_skb = skb;
+               }
+
+               skb->len = (bch->rx_ptr - skb->data) + z1;
+
+               /* HDLC length check */
+               if ((bch->mode == L1_MODE_HDLC) &&
+                   ((hdlc_complete && (skb->len < 4)) ||
+                    (skb->len > (MAX_B_FRAME_SIZE + 3)))) {
+
+                       skb->len = 0;
+                       bch->rx_ptr = skb->data;
+                       Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);  /* reset fifo */
+                       wait_busy(l1->hw);
+                       return;
+               }
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+               SetRegAddr(l1->hw, A_FIFO_DATA0);
+#endif
+
+               while (z1 >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+                       *((unsigned long *) bch->rx_ptr) =
+                           Read_hfc32(l1->hw, A_FIFO_DATA0);
+#else
+                       *((unsigned long *) bch->rx_ptr) =
+                           fRead_hfc32(l1->hw);
+#endif
+                       bch->rx_ptr += 4;
+                       z1 -= 4;
+               }
+
+               while (z1--)
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+                       *(bch->rx_ptr++) = Read_hfc8(l1->hw, A_FIFO_DATA0);
+#else
+                       *(bch->rx_ptr++) = fRead_hfc8(l1->hw);
+#endif
+
+               if (hdlc_complete) {
+                       /* increment f counter */
+                       Write_hfc8(l1->hw, A_INC_RES_FIFO, 1);
+                       wait_busy(l1->hw);
+
+                       /* hdlc crc check */
+                       bch->rx_ptr--;
+                       if (*bch->rx_ptr) {
+                               skb->len = 0;
+                               bch->rx_ptr = skb->data;
+                               continue;
+                       }
+                       skb->len -= 3;
+               }
+               if (hdlc_complete || (bch->mode == L1_MODE_TRANS)) {
+                       bch->rx_skb = NULL;
+                       bch->rx_ptr = NULL;
+                       bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+                                          PH_DATA | INDICATION, skb);
+               }
+
+       } while (1);
+}                              /* rx_b_frame */
+
+/********************************************/
+/* a D-frame has been/should be transmitted */
+/********************************************/
+static void
+tx_d_frame(struct hfc4s8s_l1 *l1p)
+{
+       struct sk_buff *skb;
+       u_char f1, f2;
+       u_char *cp;
+       int cnt;
+
+       if (l1p->l1_state != 7)
+               return;
+
+       /* TX fifo */
+       Write_hfc8(l1p->hw, R_FIFO, (l1p->st_num * 8 + 4));
+       wait_busy(l1p->hw);
+
+       f1 = Read_hfc8(l1p->hw, A_F1);
+       f2 = Read_hfc8_stable(l1p->hw, A_F2);
+
+       if ((f1 ^ f2) & MAX_F_CNT)
+               return;         /* fifo is still filled */
+
+       if (l1p->tx_cnt > 0) {
+               cnt = l1p->tx_cnt;
+               l1p->tx_cnt = 0;
+               l1p->d_if.ifc.l1l2(&l1p->d_if.ifc, PH_DATA | CONFIRM,
+                                  (void *) cnt);
+       }
+
+       if ((skb = skb_dequeue(&l1p->d_tx_queue))) {
+               cp = skb->data;
+               cnt = skb->len;
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+               SetRegAddr(l1p->hw, A_FIFO_DATA0);
+#endif
+
+               while (cnt >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+                       fWrite_hfc32(l1p->hw, A_FIFO_DATA0,
+                                    *(unsigned long *) cp);
+#else
+                       SetRegAddr(l1p->hw, A_FIFO_DATA0);
+                       fWrite_hfc32(l1p->hw, *(unsigned long *) cp);
+#endif
+                       cp += 4;
+                       cnt -= 4;
+               }
+
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+               while (cnt--)
+                       fWrite_hfc8(l1p->hw, A_FIFO_DATA0, *cp++);
+#else
+               while (cnt--)
+                       fWrite_hfc8(l1p->hw, *cp++);
+#endif
+
+               l1p->tx_cnt = skb->truesize;
+               Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1); /* increment f counter */
+               wait_busy(l1p->hw);
+
+               dev_kfree_skb(skb);
+       }
+}                              /* tx_d_frame */
+
+/******************************************************/
+/* a B-frame may be transmitted (or is not completed) */
+/******************************************************/
+static void
+tx_b_frame(struct hfc4s8s_btype *bch)
+{
+       struct sk_buff *skb;
+       struct hfc4s8s_l1 *l1 = bch->l1p;
+       u_char *cp;
+       int cnt, max, hdlc_num, ack_len = 0;
+
+       if (!l1->enabled || (bch->mode == L1_MODE_NULL))
+               return;
+
+       /* TX fifo */
+       Write_hfc8(l1->hw, R_FIFO,
+                  (l1->st_num * 8 + ((bch->bchan == 1) ? 0 : 2)));
+       wait_busy(l1->hw);
+       do {
+
+               if (bch->mode == L1_MODE_HDLC) {
+                       hdlc_num = Read_hfc8(l1->hw, A_F1) & MAX_F_CNT;
+                       hdlc_num -=
+                           (Read_hfc8_stable(l1->hw, A_F2) & MAX_F_CNT);
+                       if (hdlc_num < 0)
+                               hdlc_num += 16;
+                       if (hdlc_num >= 15)
+                               break;  /* fifo still filled up with hdlc frames */
+               } else
+                       hdlc_num = 0;
+
+               if (!(skb = bch->tx_skb)) {
+                       if (!(skb = skb_dequeue(&bch->tx_queue))) {
+                               l1->hw->mr.fifo_slow_timer_service[l1->
+                                                                  st_num]
+                                   &= ~((bch->bchan == 1) ? 1 : 4);
+                               break;  /* list empty */
+                       }
+                       bch->tx_skb = skb;
+                       bch->tx_cnt = 0;
+               }
+
+               if (!hdlc_num)
+                       l1->hw->mr.fifo_slow_timer_service[l1->st_num] |=
+                           ((bch->bchan == 1) ? 1 : 4);
+               else
+                       l1->hw->mr.fifo_slow_timer_service[l1->st_num] &=
+                           ~((bch->bchan == 1) ? 1 : 4);
+
+               max = Read_hfc16_stable(l1->hw, A_Z2);
+               max -= Read_hfc16(l1->hw, A_Z1);
+               if (max <= 0)
+                       max += 384;
+               max--;
+
+               if (max < 16)
+                       break;  /* don't write to small amounts of bytes */
+
+               cnt = skb->len - bch->tx_cnt;
+               if (cnt > max)
+                       cnt = max;
+               cp = skb->data + bch->tx_cnt;
+               bch->tx_cnt += cnt;
+
+#ifndef CONFIG_HISAX_HFC4S8S_PCIMEM
+               SetRegAddr(l1->hw, A_FIFO_DATA0);
+#endif
+               while (cnt >= 4) {
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+                       fWrite_hfc32(l1->hw, A_FIFO_DATA0,
+                                    *(unsigned long *) cp);
+#else
+                       fWrite_hfc32(l1->hw, *(unsigned long *) cp);
+#endif
+                       cp += 4;
+                       cnt -= 4;
+               }
+
+               while (cnt--)
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+                       fWrite_hfc8(l1->hw, A_FIFO_DATA0, *cp++);
+#else
+                       fWrite_hfc8(l1->hw, *cp++);
+#endif
+
+               if (bch->tx_cnt >= skb->len) {
+                       if (bch->mode == L1_MODE_HDLC) {
+                               /* increment f counter */
+                               Write_hfc8(l1->hw, A_INC_RES_FIFO, 1);
+                       }
+                       ack_len += skb->truesize;
+                       bch->tx_skb = 0;
+                       bch->tx_cnt = 0;
+                       dev_kfree_skb(skb);
+               } else
+                       /* Re-Select */
+                       Write_hfc8(l1->hw, R_FIFO,
+                                  (l1->st_num * 8 +
+                                   ((bch->bchan == 1) ? 0 : 2)));
+               wait_busy(l1->hw);
+       } while (1);
+
+       if (ack_len)
+               bch->b_if.ifc.l1l2((struct hisax_if *) &bch->b_if,
+                                  PH_DATA | CONFIRM, (void *) ack_len);
+}                              /* tx_b_frame */
+
+/*************************************/
+/* bottom half handler for interrupt */
+/*************************************/
+static void
+hfc4s8s_bh(hfc4s8s_hw * hw)
+{
+       u_char b;
+       struct hfc4s8s_l1 *l1p;
+       volatile u_char *fifo_stat;
+       int idx;
+
+       /* handle layer 1 state changes */
+       b = 1;
+       l1p = hw->l1;
+       while (b) {
+               if ((b & hw->mr.r_irq_statech)) {
+                       /* reset l1 event */
+                       hw->mr.r_irq_statech &= ~b;
+                       if (l1p->enabled) {
+                               if (l1p->nt_mode) {
+                                       u_char oldstate = l1p->l1_state;
+
+                                       Write_hfc8(l1p->hw, R_ST_SEL,
+                                                  l1p->st_num);
+                                       l1p->l1_state =
+                                           Read_hfc8(l1p->hw,
+                                                     A_ST_RD_STA) & 0xf;
+
+                                       if ((oldstate == 3)
+                                           && (l1p->l1_state != 3))
+                                               l1p->d_if.ifc.l1l2(&l1p->
+                                                                  d_if.
+                                                                  ifc,
+                                                                  PH_DEACTIVATE
+                                                                  |
+                                                                  INDICATION,
+                                                                  NULL);
+
+                                       if (l1p->l1_state != 2) {
+                                               del_timer(&l1p->l1_timer);
+                                               if (l1p->l1_state == 3) {
+                                                       l1p->d_if.ifc.
+                                                           l1l2(&l1p->
+                                                                d_if.ifc,
+                                                                PH_ACTIVATE
+                                                                |
+                                                                INDICATION,
+                                                                NULL);
+                                               }
+                                       } else {
+                                               /* allow transition */
+                                               Write_hfc8(hw, A_ST_WR_STA,
+                                                          M_SET_G2_G3);
+                                               mod_timer(&l1p->l1_timer,
+                                                         jiffies +
+                                                         L1_TIMER_T1);
+                                       }
+                                       printk(KERN_INFO
+                                              "HFC-4S/8S: NT ch %d l1 state %d -> %d\n",
+                                              l1p->st_num, oldstate,
+                                              l1p->l1_state);
+                               } else {
+                                       u_char oldstate = l1p->l1_state;
+
+                                       Write_hfc8(l1p->hw, R_ST_SEL,
+                                                  l1p->st_num);
+                                       l1p->l1_state =
+                                           Read_hfc8(l1p->hw,
+                                                     A_ST_RD_STA) & 0xf;
+
+                                       if (((l1p->l1_state == 3) &&
+                                            ((oldstate == 7) ||
+                                             (oldstate == 8))) ||
+                                           ((timer_pending
+                                             (&l1p->l1_timer))
+                                            && (l1p->l1_state == 8))) {
+                                               mod_timer(&l1p->l1_timer,
+                                                         L1_TIMER_T4 +
+                                                         jiffies);
+                                       } else {
+                                               if (l1p->l1_state == 7) {
+                                                       del_timer(&l1p->
+                                                                 l1_timer);
+                                                       l1p->d_if.ifc.
+                                                           l1l2(&l1p->
+                                                                d_if.ifc,
+                                                                PH_ACTIVATE
+                                                                |
+                                                                INDICATION,
+                                                                NULL);
+                                                       tx_d_frame(l1p);
+                                               }
+                                               if (l1p->l1_state == 3) {
+                                                       if (oldstate != 3)
+                                                               l1p->d_if.
+                                                                   ifc.
+                                                                   l1l2
+                                                                   (&l1p->
+                                                                    d_if.
+                                                                    ifc,
+                                                                    PH_DEACTIVATE
+                                                                    |
+                                                                    INDICATION,
+                                                                    NULL);
+                                               }
+                                       }
+                                       printk(KERN_INFO
+                                              "HFC-4S/8S: TE %d ch %d l1 state %d -> %d\n",
+                                              l1p->hw->cardnum,
+                                              l1p->st_num, oldstate,
+                                              l1p->l1_state);
+                               }
+                       }
+               }
+               b <<= 1;
+               l1p++;
+       }
+
+       /* now handle the fifos */
+       idx = 0;
+       fifo_stat = hw->mr.r_irq_fifo_blx;
+       l1p = hw->l1;
+       while (idx < hw->driver_data.max_st_ports) {
+
+               if (hw->mr.timer_irq) {
+                       *fifo_stat |= hw->mr.fifo_rx_trans_enables[idx];
+                       if (hw->fifo_sched_cnt <= 0) {
+                               *fifo_stat |=
+                                   hw->mr.fifo_slow_timer_service[l1p->
+                                                                  st_num];
+                       }
+               }
+               /* ignore fifo 6 (TX E fifo) */
+               *fifo_stat &= 0xff - 0x40;
+
+               while (*fifo_stat) {
+
+                       if (!l1p->nt_mode) {
+                               /* RX Fifo has data to read */
+                               if ((*fifo_stat & 0x20)) {
+                                       *fifo_stat &= ~0x20;
+                                       rx_d_frame(l1p, 0);
+                               }
+                               /* E Fifo has data to read */
+                               if ((*fifo_stat & 0x80)) {
+                                       *fifo_stat &= ~0x80;
+                                       rx_d_frame(l1p, 1);
+                               }
+                               /* TX Fifo completed send */
+                               if ((*fifo_stat & 0x10)) {
+                                       *fifo_stat &= ~0x10;
+                                       tx_d_frame(l1p);
+                               }
+                       }
+                       /* B1 RX Fifo has data to read */
+                       if ((*fifo_stat & 0x2)) {
+                               *fifo_stat &= ~0x2;
+                               rx_b_frame(l1p->b_ch);
+                       }
+                       /* B1 TX Fifo has send completed */
+                       if ((*fifo_stat & 0x1)) {
+                               *fifo_stat &= ~0x1;
+                               tx_b_frame(l1p->b_ch);
+                       }
+                       /* B2 RX Fifo has data to read */
+                       if ((*fifo_stat & 0x8)) {
+                               *fifo_stat &= ~0x8;
+                               rx_b_frame(l1p->b_ch + 1);
+                       }
+                       /* B2 TX Fifo has send completed */
+                       if ((*fifo_stat & 0x4)) {
+                               *fifo_stat &= ~0x4;
+                               tx_b_frame(l1p->b_ch + 1);
+                       }
+               }
+               fifo_stat++;
+               l1p++;
+               idx++;
+       }
+
+       if (hw->fifo_sched_cnt <= 0)
+               hw->fifo_sched_cnt += (1 << (7 - TRANS_TIMER_MODE));
+       hw->mr.timer_irq = 0;   /* clear requested timer irq */
+}                              /* hfc4s8s_bh */
+
+/*********************/
+/* interrupt handler */
+/*********************/
+static irqreturn_t
+hfc4s8s_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+       hfc4s8s_hw *hw = dev_id;
+       u_char b, ovr;
+       volatile u_char *ovp;
+       int idx;
+       u_char old_ioreg;
+
+       if (!hw || !(hw->mr.r_irq_ctrl & M_GLOB_IRQ_EN))
+               return IRQ_NONE;
+
+#ifndef        CONFIG_HISAX_HFC4S8S_PCIMEM
+       /* read current selected regsister */
+       old_ioreg = GetRegAddr(hw);
+#endif
+
+       /* Layer 1 State change */
+       hw->mr.r_irq_statech |=
+           (Read_hfc8(hw, R_SCI) & hw->mr.r_irqmsk_statchg);
+       if (!
+           (b = (Read_hfc8(hw, R_STATUS) & (M_MISC_IRQSTA | M_FR_IRQSTA)))
+&& !hw->mr.r_irq_statech) {
+#ifndef        CONFIG_HISAX_HFC4S8S_PCIMEM
+               SetRegAddr(hw, old_ioreg);
+#endif
+               return IRQ_NONE;
+       }
+
+       /* timer event */
+       if (Read_hfc8(hw, R_IRQ_MISC) & M_TI_IRQ) {
+               hw->mr.timer_irq = 1;
+               hw->fifo_sched_cnt--;
+       }
+
+       /* FIFO event */
+       if ((ovr = Read_hfc8(hw, R_IRQ_OVIEW))) {
+               hw->mr.r_irq_oview |= ovr;
+               idx = R_IRQ_FIFO_BL0;
+               ovp = hw->mr.r_irq_fifo_blx;
+               while (ovr) {
+                       if ((ovr & 1)) {
+                               *ovp |= Read_hfc8(hw, idx);
+                       }
+                       ovp++;
+                       idx++;
+                       ovr >>= 1;
+               }
+       }
+
+       /* queue the request to allow other cards to interrupt */
+       schedule_work(&hw->tqueue);
+
+#ifndef        CONFIG_HISAX_HFC4S8S_PCIMEM
+       SetRegAddr(hw, old_ioreg);
+#endif
+       return IRQ_HANDLED;
+}                              /* hfc4s8s_interrupt */
+
+/***********************************************************************/
+/* reset the complete chip, don't release the chips irq but disable it */
+/***********************************************************************/
+static void
+chipreset(hfc4s8s_hw * hw)
+{
+       u_long flags;
+
+       spin_lock_irqsave(&hw->lock, flags);
+       Write_hfc8(hw, R_CTRL, 0);      /* use internal RAM */
+       Write_hfc8(hw, R_RAM_MISC, 0);  /* 32k*8 RAM */
+       Write_hfc8(hw, R_FIFO_MD, 0);   /* fifo mode 386 byte/fifo simple mode */
+       Write_hfc8(hw, R_CIRM, M_SRES); /* reset chip */
+       hw->mr.r_irq_ctrl = 0;  /* interrupt is inactive */
+       spin_unlock_irqrestore(&hw->lock, flags);
+
+       udelay(3);
+       Write_hfc8(hw, R_CIRM, 0);      /* disable reset */
+       wait_busy(hw);
+
+       Write_hfc8(hw, R_PCM_MD0, M_PCM_MD);    /* master mode */
+       Write_hfc8(hw, R_RAM_MISC, M_FZ_MD);    /* transmit fifo option */
+       if (hw->driver_data.clock_mode == 1)
+               Write_hfc8(hw, R_BRG_PCM_CFG, M_PCM_CLK);       /* PCM clk / 2 */
+       Write_hfc8(hw, R_TI_WD, TRANS_TIMER_MODE);      /* timer interval */
+
+       memset(&hw->mr, 0, sizeof(hw->mr));
+}                              /* chipreset */
+
+/********************************************/
+/* disable/enable hardware in nt or te mode */
+/********************************************/
+void
+hfc_hardware_enable(hfc4s8s_hw * hw, int enable, int nt_mode)
+{
+       u_long flags;
+       char if_name[40];
+       int i;
+
+       if (enable) {
+               /* save system vars */
+               hw->nt_mode = nt_mode;
+
+               /* enable fifo and state irqs, but not global irq enable */
+               hw->mr.r_irq_ctrl = M_FIFO_IRQ;
+               Write_hfc8(hw, R_IRQ_CTRL, hw->mr.r_irq_ctrl);
+               hw->mr.r_irqmsk_statchg = 0;
+               Write_hfc8(hw, R_SCI_MSK, hw->mr.r_irqmsk_statchg);
+               Write_hfc8(hw, R_PWM_MD, 0x80);
+               Write_hfc8(hw, R_PWM1, 26);
+               if (!nt_mode)
+                       Write_hfc8(hw, R_ST_SYNC, M_AUTO_SYNC);
+
+               /* enable the line interfaces and fifos */
+               for (i = 0; i < hw->driver_data.max_st_ports; i++) {
+                       hw->mr.r_irqmsk_statchg |= (1 << i);
+                       Write_hfc8(hw, R_SCI_MSK, hw->mr.r_irqmsk_statchg);
+                       Write_hfc8(hw, R_ST_SEL, i);
+                       Write_hfc8(hw, A_ST_CLK_DLY,
+                                  ((nt_mode) ? CLKDEL_NT : CLKDEL_TE));
+                       hw->mr.r_ctrl0 = ((nt_mode) ? CTRL0_NT : CTRL0_TE);
+                       Write_hfc8(hw, A_ST_CTRL0, hw->mr.r_ctrl0);
+                       Write_hfc8(hw, A_ST_CTRL2, 3);
+                       Write_hfc8(hw, A_ST_WR_STA, 0); /* enable state machine */
+
+                       hw->l1[i].enabled = 1;
+                       hw->l1[i].nt_mode = nt_mode;
+
+                       if (!nt_mode) {
+                               /* setup E-fifo */
+                               Write_hfc8(hw, R_FIFO, i * 8 + 7);      /* E fifo */
+                               wait_busy(hw);
+                               Write_hfc8(hw, A_CON_HDLC, 0x11);       /* HDLC mode, 1 fill, connect ST */
+                               Write_hfc8(hw, A_SUBCH_CFG, 2); /* only 2 bits */
+                               Write_hfc8(hw, A_IRQ_MSK, 1);   /* enable interrupt */
+                               Write_hfc8(hw, A_INC_RES_FIFO, 2);      /* reset fifo */
+                               wait_busy(hw);
+
+                               /* setup D RX-fifo */
+                               Write_hfc8(hw, R_FIFO, i * 8 + 5);      /* RX fifo */
+                               wait_busy(hw);
+                               Write_hfc8(hw, A_CON_HDLC, 0x11);       /* HDLC mode, 1 fill, connect ST */
+                               Write_hfc8(hw, A_SUBCH_CFG, 2); /* only 2 bits */
+                               Write_hfc8(hw, A_IRQ_MSK, 1);   /* enable interrupt */
+                               Write_hfc8(hw, A_INC_RES_FIFO, 2);      /* reset fifo */
+                               wait_busy(hw);
+
+                               /* setup D TX-fifo */
+                               Write_hfc8(hw, R_FIFO, i * 8 + 4);      /* TX fifo */
+                               wait_busy(hw);
+                               Write_hfc8(hw, A_CON_HDLC, 0x11);       /* HDLC mode, 1 fill, connect ST */
+                               Write_hfc8(hw, A_SUBCH_CFG, 2); /* only 2 bits */
+                               Write_hfc8(hw, A_IRQ_MSK, 1);   /* enable interrupt */
+                               Write_hfc8(hw, A_INC_RES_FIFO, 2);      /* reset fifo */
+                               wait_busy(hw);
+                       }
+
+                       sprintf(if_name, "hfc4s8s_%d%d_", hw->cardnum, i);
+
+                       if (hisax_register
+                           (&hw->l1[i].d_if, hw->l1[i].b_table, if_name,
+                            ((nt_mode) ? 3 : 2))) {
+
+                               hw->l1[i].enabled = 0;
+                               hw->mr.r_irqmsk_statchg &= ~(1 << i);
+                               Write_hfc8(hw, R_SCI_MSK,
+                                          hw->mr.r_irqmsk_statchg);
+                               printk(KERN_INFO
+                                      "HFC-4S/8S: Unable to register S/T device %s, break\n",
+                                      if_name);
+                               break;
+                       }
+               }
+               spin_lock_irqsave(&hw->lock, flags);
+               hw->mr.r_irq_ctrl |= M_GLOB_IRQ_EN;
+               Write_hfc8(hw, R_IRQ_CTRL, hw->mr.r_irq_ctrl);
+               spin_unlock_irqrestore(&hw->lock, flags);
+       } else {
+               /* disable hardware */
+               spin_lock_irqsave(&hw->lock, flags);
+               hw->mr.r_irq_ctrl &= ~M_GLOB_IRQ_EN;
+               Write_hfc8(hw, R_IRQ_CTRL, hw->mr.r_irq_ctrl);
+               spin_unlock_irqrestore(&hw->lock, flags);
+
+               for (i = hw->driver_data.max_st_ports - 1; i >= 0; i--) {
+                       hw->l1[i].enabled = 0;
+                       hisax_unregister(&hw->l1[i].d_if);
+                       del_timer(&hw->l1[i].l1_timer);
+                       skb_queue_purge(&hw->l1[i].d_tx_queue);
+                       skb_queue_purge(&hw->l1[i].b_ch[0].tx_queue);
+                       skb_queue_purge(&hw->l1[i].b_ch[1].tx_queue);
+               }
+               chipreset(hw);
+       }
+}                              /* hfc_hardware_enable */
+
+/******************************************/
+/* disable memory mapped ports / io ports */
+/******************************************/
+void
+release_pci_ports(hfc4s8s_hw * hw)
+{
+       pci_write_config_word(hw->pdev, PCI_COMMAND, 0);
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+       if (hw->membase)
+               iounmap((void *) hw->membase);
+#else
+       if (hw->iobase)
+               release_region(hw->iobase, 8);
+#endif
+}
+
+/*****************************************/
+/* enable memory mapped ports / io ports */
+/*****************************************/
+void
+enable_pci_ports(hfc4s8s_hw * hw)
+{
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+       pci_write_config_word(hw->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
+#else
+       pci_write_config_word(hw->pdev, PCI_COMMAND, PCI_ENA_REGIO);
+#endif
+}
+
+/*************************************/
+/* initialise the HFC-4s/8s hardware */
+/* return 0 on success.              */
+/*************************************/
+static int __devinit
+setup_instance(hfc4s8s_hw * hw)
+{
+       int err = -EIO;
+       int i;
+
+       for (i = 0; i < HFC_MAX_ST; i++) {
+               struct hfc4s8s_l1 *l1p;
+
+               l1p = hw->l1 + i;
+               spin_lock_init(&l1p->lock);
+               l1p->hw = hw;
+               l1p->l1_timer.function = (void *) hfc_l1_timer;
+               l1p->l1_timer.data = (long) (l1p);
+               init_timer(&l1p->l1_timer);
+               l1p->st_num = i;
+               skb_queue_head_init(&l1p->d_tx_queue);
+               l1p->d_if.ifc.priv = hw->l1 + i;
+               l1p->d_if.ifc.l2l1 = (void *) dch_l2l1;
+
+               spin_lock_init(&l1p->b_ch[0].lock);
+               l1p->b_ch[0].b_if.ifc.l2l1 = (void *) bch_l2l1;
+               l1p->b_ch[0].b_if.ifc.priv = (void *) &l1p->b_ch[0];
+               l1p->b_ch[0].l1p = hw->l1 + i;
+               l1p->b_ch[0].bchan = 1;
+               l1p->b_table[0] = &l1p->b_ch[0].b_if;
+               skb_queue_head_init(&l1p->b_ch[0].tx_queue);
+
+               spin_lock_init(&l1p->b_ch[1].lock);
+               l1p->b_ch[1].b_if.ifc.l2l1 = (void *) bch_l2l1;
+               l1p->b_ch[1].b_if.ifc.priv = (void *) &l1p->b_ch[1];
+               l1p->b_ch[1].l1p = hw->l1 + i;
+               l1p->b_ch[1].bchan = 2;
+               l1p->b_table[1] = &l1p->b_ch[1].b_if;
+               skb_queue_head_init(&l1p->b_ch[1].tx_queue);
+       }
+
+       enable_pci_ports(hw);
+       chipreset(hw);
+
+       i = Read_hfc8(hw, R_CHIP_ID) >> CHIP_ID_SHIFT;
+       if (i != hw->driver_data.chip_id) {
+               printk(KERN_INFO
+                      "HFC-4S/8S: invalid chip id 0x%x instead of 0x%x, card ignored\n",
+                      i, hw->driver_data.chip_id);
+               goto out;
+       }
+
+       i = Read_hfc8(hw, R_CHIP_RV) & 0xf;
+       if (!i) {
+               printk(KERN_INFO
+                      "HFC-4S/8S: chip revision 0 not supported, card ignored\n");
+               goto out;
+       }
+
+       INIT_WORK(&hw->tqueue, (void *) (void *) hfc4s8s_bh, hw);
+
+       if (request_irq
+           (hw->irq, hfc4s8s_interrupt, SA_SHIRQ, hw->card_name, hw)) {
+               printk(KERN_INFO
+                      "HFC-4S/8S: unable to alloc irq %d, card ignored\n",
+                      hw->irq);
+               goto out;
+       }
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+       printk(KERN_INFO
+              "HFC-4S/8S: found PCI card at membase 0x%p, irq %d\n",
+              hw->hw_membase, hw->irq);
+#else
+       printk(KERN_INFO
+              "HFC-4S/8S: found PCI card at iobase 0x%x, irq %d\n",
+              hw->iobase, hw->irq);
+#endif
+
+       hfc_hardware_enable(hw, 1, 0);
+
+       return (0);
+
+      out:
+       hw->irq = 0;
+       release_pci_ports(hw);
+       kfree(hw);
+       return (err);
+}
+
+/*****************************************/
+/* PCI hotplug interface: probe new card */
+/*****************************************/
+static int __devinit
+hfc4s8s_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       int err = -ENOMEM;
+       hfc4s8s_param *driver_data = (hfc4s8s_param *) ent->driver_data;
+       hfc4s8s_hw *hw;
+
+       if (!(hw = kmalloc(sizeof(hfc4s8s_hw), GFP_ATOMIC))) {
+               printk(KERN_ERR "No kmem for HFC-4S/8S card\n");
+               return (err);
+       }
+       memset(hw, 0, sizeof(hfc4s8s_hw));
+
+       hw->pdev = pdev;
+       err = pci_enable_device(pdev);
+
+       if (err)
+               goto out;
+
+       hw->cardnum = card_cnt;
+       sprintf(hw->card_name, "hfc4s8s_%d", hw->cardnum);
+       printk(KERN_INFO "HFC-4S/8S: found adapter %s (%s) at %s\n",
+              driver_data->device_name, hw->card_name, pci_name(pdev));
+
+       spin_lock_init(&hw->lock);
+
+       hw->driver_data = *driver_data;
+       hw->irq = pdev->irq;
+       hw->iobase = pci_resource_start(pdev, 0);
+
+#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM
+       hw->hw_membase = (u_char *) pci_resource_start(pdev, 1);
+       hw->membase = ioremap((ulong) hw->hw_membase, 256);
+#else
+       if (!request_region(hw->iobase, 8, hw->card_name)) {
+               printk(KERN_INFO
+                      "HFC-4S/8S: failed to rquest address space at 0x%04x\n",
+                      hw->iobase);
+               goto out;
+       }
+#endif
+
+       pci_set_drvdata(pdev, hw);
+       err = setup_instance(hw);
+       if (!err)
+               card_cnt++;
+       return (err);
+
+      out:
+       kfree(hw);
+       return (err);
+}
+
+/**************************************/
+/* PCI hotplug interface: remove card */
+/**************************************/
+static void __devexit
+hfc4s8s_remove(struct pci_dev *pdev)
+{
+       hfc4s8s_hw *hw = pci_get_drvdata(pdev);
+
+       printk(KERN_INFO "HFC-4S/8S: removing card %d\n", hw->cardnum);
+       hfc_hardware_enable(hw, 0, 0);
+
+       if (hw->irq)
+               free_irq(hw->irq, hw);
+       hw->irq = 0;
+       release_pci_ports(hw);
+
+       card_cnt--;
+       pci_disable_device(pdev);
+       kfree(hw);
+       return;
+}
+
+static struct pci_driver hfc4s8s_driver = {
+      name:"hfc4s8s_l1",
+      probe:hfc4s8s_probe,
+      remove:__devexit_p(hfc4s8s_remove),
+      id_table:hfc4s8s_ids,
+};
+
+/**********************/
+/* driver Module init */
+/**********************/
+static int __init
+hfc4s8s_module_init(void)
+{
+       int err;
+
+       printk(KERN_INFO
+              "HFC-4S/8S: Layer 1 driver module for HFC-4S/8S isdn chips, %s\n",
+              hfc4s8s_rev);
+       printk(KERN_INFO
+              "HFC-4S/8S: (C) 2003 Cornelius Consult, www.cornelius-consult.de\n");
+
+       card_cnt = 0;
+
+       err = pci_register_driver(&hfc4s8s_driver);
+       if (err < 0) {
+               goto out;
+       }
+       printk(KERN_INFO "HFC-4S/8S: found %d cards\n", card_cnt);
+
+#if !defined(CONFIG_HOTPLUG)
+       if (err == 0) {
+               err = -ENODEV;
+               pci_unregister_driver(&hfc4s8s_driver);
+               goto out;
+       }
+#endif
+
+       return 0;
+      out:
+       return (err);
+}                              /* hfc4s8s_init_hw */
+
+/*************************************/
+/* driver module exit :              */
+/* release the HFC-4s/8s hardware    */
+/*************************************/
+static void
+hfc4s8s_module_exit(void)
+{
+       pci_unregister_driver(&hfc4s8s_driver);
+       printk(KERN_INFO "HFC-4S/8S: module removed\n");
+}                              /* hfc4s8s_release_hw */
+
+module_init(hfc4s8s_module_init);
+module_exit(hfc4s8s_module_exit);
diff --git a/drivers/isdn/hisax/hfc4s8s_l1.h b/drivers/isdn/hisax/hfc4s8s_l1.h
new file mode 100644 (file)
index 0000000..e8f9c07
--- /dev/null
@@ -0,0 +1,88 @@
+/***************************************************************/
+/*  $Id: hfc4s8s_l1.h,v 1.1 2005/02/02 17:28:55 martinb1 Exp $ */
+/*                                                             */
+/*  This file is a minimal required extraction of hfc48scu.h   */
+/*  (Genero 3.2, HFC XML 1.7a for HFC-E1, HFC-4S and HFC-8S)   */
+/*                                                             */
+/*  To get this complete register description contact          */
+/*  Cologne Chip AG :                                          */
+/*  Internet:  http://www.colognechip.com/                     */
+/*  E-Mail:    info@colognechip.com                            */
+/***************************************************************/
+
+#ifndef _HFC4S8S_L1_H_
+#define _HFC4S8S_L1_H_
+
+
+/*
+*  include Genero generated HFC-4S/8S header file hfc48scu.h
+*  for comlete register description. This will define _HFC48SCU_H_
+*  to prevent redefinitions
+*/
+
+// #include "hfc48scu.h"
+
+#ifndef _HFC48SCU_H_
+#define _HFC48SCU_H_
+
+#ifndef PCI_VENDOR_ID_CCD
+#define PCI_VENDOR_ID_CCD      0x1397
+#endif
+
+#define CHIP_ID_4S             0x0C
+#define CHIP_ID_8S             0x08
+#define PCI_DEVICE_ID_4S       0x08B4
+#define PCI_DEVICE_ID_8S       0x16B8
+
+#define R_IRQ_MISC     0x11
+#define M_TI_IRQ       0x02
+#define A_ST_RD_STA    0x30
+#define A_ST_WR_STA    0x30
+#define M_SET_G2_G3    0x80
+#define A_ST_CTRL0     0x31
+#define A_ST_CTRL2     0x33
+#define A_ST_CLK_DLY   0x37
+#define A_Z1           0x04
+#define A_Z2           0x06
+#define R_CIRM         0x00
+#define M_SRES         0x08
+#define R_CTRL         0x01
+#define R_BRG_PCM_CFG  0x02
+#define M_PCM_CLK      0x20
+#define R_RAM_MISC     0x0C
+#define M_FZ_MD                0x80
+#define R_FIFO_MD      0x0D
+#define A_INC_RES_FIFO 0x0E
+#define R_FIFO         0x0F
+#define A_F1           0x0C
+#define A_F2           0x0D
+#define R_IRQ_OVIEW    0x10
+#define R_CHIP_ID      0x16
+#define R_STATUS       0x1C
+#define M_BUSY         0x01
+#define M_MISC_IRQSTA  0x40
+#define M_FR_IRQSTA    0x80
+#define R_CHIP_RV      0x1F
+#define R_IRQ_CTRL     0x13
+#define M_FIFO_IRQ     0x01
+#define M_GLOB_IRQ_EN  0x08
+#define R_PCM_MD0      0x14
+#define M_PCM_MD       0x01
+#define A_FIFO_DATA0   0x80
+#define R_TI_WD                0x1A
+#define R_PWM1         0x39
+#define R_PWM_MD       0x46
+#define R_IRQ_FIFO_BL0 0xC8
+#define A_CON_HDLC     0xFA
+#define A_SUBCH_CFG    0xFB
+#define A_IRQ_MSK      0xFF
+#define R_SCI_MSK      0x12
+#define R_ST_SEL       0x16
+#define R_ST_SYNC      0x17
+#define M_AUTO_SYNC    0x08
+#define R_SCI          0x12
+#define R_IRQMSK_MISC  0x11
+#define M_TI_IRQMSK    0x02
+
+#endif /* _HFC4S8S_L1_H_ */
+#endif /* _HFC48SCU_H_ */
diff --git a/drivers/isdn/hisax/hfc_usb.h b/drivers/isdn/hisax/hfc_usb.h
new file mode 100644 (file)
index 0000000..b171600
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+* hfc_usb.h
+*
+* $Id: hfc_usb.h,v 4.1 2005/01/26 17:25:53 martinb1 Exp $
+*/
+
+#ifndef __HFC_USB_H__
+#define __HFC_USB_H__
+
+#define DRIVER_AUTHOR   "Peter Sprenger (sprenger@moving-byters.de)"
+#define DRIVER_DESC     "HFC-S USB based HiSAX ISDN driver"
+
+#define VERBOSE_USB_DEBUG
+
+#define TRUE  1
+#define FALSE 0
+
+
+/***********/
+/* defines */
+/***********/
+#define HFC_CTRL_TIMEOUT 20    /* 5ms timeout writing/reading regs */
+#define HFC_TIMER_T3 8000      /* timeout for l1 activation timer */
+#define HFC_TIMER_T4 500       /* time for state change interval */
+
+#define HFCUSB_L1_STATECHANGE 0        /* L1 state changed */
+#define HFCUSB_L1_DRX 1                /* D-frame received */
+#define HFCUSB_L1_ERX 2                /* E-frame received */
+#define HFCUSB_L1_DTX 4                /* D-frames completed */
+
+#define MAX_BCH_SIZE 2048      /* allowed B-channel packet size */
+
+#define HFCUSB_RX_THRESHOLD 64 /* threshold for fifo report bit rx */
+#define HFCUSB_TX_THRESHOLD 64 /* threshold for fifo report bit tx */
+
+#define HFCUSB_CHIP_ID         0x16    /* Chip ID register index */
+#define HFCUSB_CIRM            0x00    /* cirm register index */
+#define HFCUSB_USB_SIZE                0x07    /* int length register */
+#define HFCUSB_USB_SIZE_I      0x06    /* iso length register */
+#define HFCUSB_F_CROSS         0x0b    /* bit order register */
+#define HFCUSB_CLKDEL          0x37    /* bit delay register */
+#define HFCUSB_CON_HDLC                0xfa    /* channel connect register */
+#define HFCUSB_HDLC_PAR                0xfb
+#define HFCUSB_SCTRL           0x31    /* S-bus control register (tx) */
+#define HFCUSB_SCTRL_E         0x32    /* same for E and special funcs */
+#define HFCUSB_SCTRL_R         0x33    /* S-bus control register (rx) */
+#define HFCUSB_F_THRES         0x0c    /* threshold register */
+#define HFCUSB_FIFO            0x0f    /* fifo select register */
+#define HFCUSB_F_USAGE         0x1a    /* fifo usage register */
+#define HFCUSB_MST_MODE0       0x14
+#define HFCUSB_MST_MODE1       0x15
+#define HFCUSB_P_DATA          0x1f
+#define HFCUSB_INC_RES_F       0x0e
+#define HFCUSB_STATES          0x30
+
+#define HFCUSB_CHIPID          0x40    /* ID value of HFC-S USB */
+
+/******************/
+/* fifo registers */
+/******************/
+#define HFCUSB_NUM_FIFOS       8       /* maximum number of fifos */
+#define HFCUSB_B1_TX           0       /* index for B1 transmit bulk/int */
+#define HFCUSB_B1_RX           1       /* index for B1 receive bulk/int */
+#define HFCUSB_B2_TX           2
+#define HFCUSB_B2_RX           3
+#define HFCUSB_D_TX            4
+#define HFCUSB_D_RX            5
+#define HFCUSB_PCM_TX          6
+#define HFCUSB_PCM_RX          7
+
+/*
+* used to switch snd_transfer_mode for different TA modes e.g. the Billion USB TA just
+* supports ISO out, while the Cologne Chip EVAL TA just supports BULK out
+*/
+#define USB_INT                0
+#define USB_BULK       1
+#define USB_ISOC       2
+
+#define ISOC_PACKETS_D 8
+#define ISOC_PACKETS_B 8
+#define ISO_BUFFER_SIZE        128
+
+// ISO send definitions
+#define SINK_MAX       68
+#define SINK_MIN       48
+#define SINK_DMIN      12
+#define SINK_DMAX      18
+#define BITLINE_INF    (-64*8)
+
+
+/**********/
+/* macros */
+/**********/
+#define write_usb(a,b,c)usb_control_msg((a)->dev,(a)->ctrl_out_pipe,0,0x40,(c),(b),0,0,HFC_CTRL_TIMEOUT)
+#define read_usb(a,b,c) usb_control_msg((a)->dev,(a)->ctrl_in_pipe,1,0xC0,0,(b),(c),1,HFC_CTRL_TIMEOUT)
+
+
+/*******************/
+/* Debugging Flags */
+/*******************/
+#define USB_DBG   1
+#define ISDN_DBG  2
+
+
+/* *********************/
+/* USB related defines */
+/***********************/
+#define HFC_CTRL_BUFSIZE 32
+
+
+
+/*************************************************/
+/* entry and size of output/input control buffer */
+/*************************************************/
+typedef struct {
+       __u8 hfc_reg;           /* register number */
+       __u8 reg_val;           /* value to be written (or read) */
+       int action;             /* data for action handler */
+} ctrl_buft;
+
+
+/********************/
+/* URB error codes: */
+/********************/
+/* Used to represent a list of values and their respective symbolic names */
+struct hfcusb_symbolic_list {
+       const int num;
+       const char *name;
+};
+
+static struct hfcusb_symbolic_list urb_errlist[] = {
+       {-ENOMEM, "No memory for allocation of internal structures"},
+       {-ENOSPC, "The host controller's bandwidth is already consumed"},
+       {-ENOENT, "URB was canceled by unlink_urb"},
+       {-EXDEV, "ISO transfer only partially completed"},
+       {-EAGAIN, "Too match scheduled for the future"},
+       {-ENXIO, "URB already queued"},
+       {-EFBIG, "Too much ISO frames requested"},
+       {-ENOSR, "Buffer error (overrun)"},
+       {-EPIPE, "Specified endpoint is stalled (device not responding)"},
+       {-EOVERFLOW, "Babble (bad cable?)"},
+       {-EPROTO, "Bit-stuff error (bad cable?)"},
+       {-EILSEQ, "CRC/Timeout"},
+       {-ETIMEDOUT, "NAK (device does not respond)"},
+       {-ESHUTDOWN, "Device unplugged"},
+       {-1, NULL}
+};
+
+
+/*****************************************************/
+/* device dependant information to support different */
+/* ISDN Ta's using the HFC-S USB chip                */
+/*****************************************************/
+
+/* USB descriptor need to contain one of the following EndPoint combination: */
+#define CNF_4INT3ISO   1       // 4 INT IN, 3 ISO OUT
+#define CNF_3INT3ISO   2       // 3 INT IN, 3 ISO OUT
+#define CNF_4ISO3ISO   3       // 4 ISO IN, 3 ISO OUT
+#define CNF_3ISO3ISO   4       // 3 ISO IN, 3 ISO OUT
+
+#define EP_NUL 1               // Endpoint at this position not allowed
+#define EP_NOP 2               // all type of endpoints allowed at this position
+#define EP_ISO 3               // Isochron endpoint mandatory at this position
+#define EP_BLK 4               // Bulk endpoint mandatory at this position
+#define EP_INT 5               // Interrupt endpoint mandatory at this position
+
+/* this array represents all endpoints possible in the HCF-USB the last
+* 3 entries are the configuration number, the minimum interval for
+* Interrupt endpoints & boolean if E-channel logging possible
+*/
+int validconf[][19] = {
+       // INT in, ISO out config
+       {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NOP, EP_INT,
+        EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL,
+        CNF_4INT3ISO, 2, 1},
+       {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_NUL,
+        EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL,
+        CNF_3INT3ISO, 2, 0},
+       // ISO in, ISO out config
+       {EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL,
+        EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NOP, EP_ISO,
+        CNF_4ISO3ISO, 2, 1},
+       {EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL,
+        EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NUL, EP_NUL,
+        CNF_3ISO3ISO, 2, 0},
+       {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}        // EOL element
+};
+
+// string description of chosen config
+char *conf_str[] = {
+       "4 Interrupt IN + 3 Isochron OUT",
+       "3 Interrupt IN + 3 Isochron OUT",
+       "4 Isochron IN + 3 Isochron OUT",
+       "3 Isochron IN + 3 Isochron OUT"
+};
+
+
+typedef struct {
+       int vendor;             // vendor id
+       int prod_id;            // product id
+       char *vend_name;        // vendor string
+       __u8 led_scheme;        // led display scheme
+       signed short led_bits[8];       // array of 8 possible LED bitmask settings
+} vendor_data;
+
+#define LED_OFF      0         // no LED support
+#define LED_SCHEME1  1         // LED standard scheme
+#define LED_SCHEME2  2         // not used yet...
+
+#define LED_POWER_ON   1
+#define LED_POWER_OFF  2
+#define LED_S0_ON      3
+#define LED_S0_OFF     4
+#define LED_B1_ON      5
+#define LED_B1_OFF     6
+#define LED_B1_DATA    7
+#define LED_B2_ON      8
+#define LED_B2_OFF     9
+#define LED_B2_DATA    10
+
+#define LED_NORMAL   0         // LEDs are normal
+#define LED_INVERTED 1         // LEDs are inverted
+
+/* time in ms to perform a Flashing LED when B-Channel has traffic */
+#define LED_TIME      250
+
+
+#endif                         // __HFC_USB_H__
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c
new file mode 100644 (file)
index 0000000..fb53573
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * PowerMac G5 SMU driver
+ *
+ * Copyright 2004 J. Mayer <l_indien@magic.fr>
+ * Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
+ *
+ * Released under the term of the GNU GPL v2.
+ */
+
+/*
+ * For now, this driver includes:
+ * - RTC get & set
+ * - reboot & shutdown commands
+ * all synchronous with IRQ disabled (ugh)
+ *
+ * TODO:
+ *   rework in a way the PMU driver works, that is asynchronous
+ *   with a queue of commands. I'll do that as soon as I have an
+ *   SMU based machine at hand. Some more cleanup is needed too,
+ *   like maybe fitting it into a platform device, etc...
+ *   Also check what's up with cache coherency, and if we really
+ *   can't do better than flushing the cache, maybe build a table
+ *   of command len/reply len like the PMU driver to only flush
+ *   what is actually necessary.
+ *   --BenH.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/bootmem.h>
+#include <linux/vmalloc.h>
+#include <linux/highmem.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/smu.h>
+#include <asm/sections.h>
+#include <asm/abs_addr.h>
+
+#define DEBUG_SMU 1
+
+#ifdef DEBUG_SMU
+#define DPRINTK(fmt, args...) do { printk(KERN_DEBUG fmt , ##args); } while (0)
+#else
+#define DPRINTK(fmt, args...) do { } while (0)
+#endif
+
+/*
+ * This is the command buffer passed to the SMU hardware
+ */
+struct smu_cmd_buf {
+       u8 cmd;
+       u8 length;
+       u8 data[0x0FFE];
+};
+
+struct smu_device {
+       spinlock_t              lock;
+       struct device_node      *of_node;
+       int                     db_ack;         /* doorbell ack GPIO */
+       int                     db_req;         /* doorbell req GPIO */
+       u32 __iomem             *db_buf;        /* doorbell buffer */
+       struct smu_cmd_buf      *cmd_buf;       /* command buffer virtual */
+       u32                     cmd_buf_abs;    /* command buffer absolute */
+};
+
+/*
+ * I don't think there will ever be more than one SMU, so
+ * for now, just hard code that
+ */
+static struct smu_device       *smu;
+
+/*
+ * SMU low level communication stuff
+ */
+static inline int smu_cmd_stat(struct smu_cmd_buf *cmd_buf, u8 cmd_ack)
+{
+       rmb();
+       return cmd_buf->cmd == cmd_ack && cmd_buf->length != 0;
+}
+
+static inline u8 smu_save_ack_cmd(struct smu_cmd_buf *cmd_buf)
+{
+       return (~cmd_buf->cmd) & 0xff;
+}
+
+static void smu_send_cmd(struct smu_device *dev)
+{
+       /* SMU command buf is currently cacheable, we need a physical
+        * address. This isn't exactly a DMA mapping here, I suspect
+        * the SMU is actually communicating with us via i2c to the
+        * northbridge or the CPU to access RAM.
+        */
+       writel(dev->cmd_buf_abs, dev->db_buf);
+
+       /* Ring the SMU doorbell */
+       pmac_do_feature_call(PMAC_FTR_WRITE_GPIO, NULL, dev->db_req, 4);
+       pmac_do_feature_call(PMAC_FTR_READ_GPIO, NULL, dev->db_req, 4);
+}
+
+static int smu_cmd_done(struct smu_device *dev)
+{
+       unsigned long wait = 0;
+       int gpio;
+
+       /* Check the SMU doorbell */
+       do  {
+               gpio = pmac_do_feature_call(PMAC_FTR_READ_GPIO,
+                                           NULL, dev->db_ack);
+               if ((gpio & 7) == 7)
+                       return 0;
+               udelay(100);
+       } while(++wait < 10000);
+
+       printk(KERN_ERR "SMU timeout !\n");
+       return -ENXIO;
+}
+
+static int smu_do_cmd(struct smu_device *dev)
+{
+       int rc;
+       u8 cmd_ack;
+
+       DPRINTK("SMU do_cmd %02x len=%d %02x\n",
+               dev->cmd_buf->cmd, dev->cmd_buf->length,
+               dev->cmd_buf->data[0]);
+
+       cmd_ack = smu_save_ack_cmd(dev->cmd_buf);
+
+       /* Clear cmd_buf cache lines */
+       flush_inval_dcache_range((unsigned long)dev->cmd_buf,
+                                ((unsigned long)dev->cmd_buf) +
+                                sizeof(struct smu_cmd_buf));
+       smu_send_cmd(dev);
+       rc = smu_cmd_done(dev);
+       if (rc == 0)
+               rc = smu_cmd_stat(dev->cmd_buf, cmd_ack) ? 0 : -1;
+
+       DPRINTK("SMU do_cmd %02x len=%d %02x => %d (%02x)\n",
+               dev->cmd_buf->cmd, dev->cmd_buf->length,
+               dev->cmd_buf->data[0], rc, cmd_ack);
+
+       return rc;
+}
+
+/* RTC low level commands */
+static inline int bcd2hex (int n)
+{
+       return (((n & 0xf0) >> 4) * 10) + (n & 0xf);
+}
+
+static inline int hex2bcd (int n)
+{
+       return ((n / 10) << 4) + (n % 10);
+}
+
+#if 0
+static inline void smu_fill_set_pwrup_timer_cmd(struct smu_cmd_buf *cmd_buf)
+{
+       cmd_buf->cmd = 0x8e;
+       cmd_buf->length = 8;
+       cmd_buf->data[0] = 0x00;
+       memset(cmd_buf->data + 1, 0, 7);
+}
+
+static inline void smu_fill_get_pwrup_timer_cmd(struct smu_cmd_buf *cmd_buf)
+{
+       cmd_buf->cmd = 0x8e;
+       cmd_buf->length = 1;
+       cmd_buf->data[0] = 0x01;
+}
+
+static inline void smu_fill_dis_pwrup_timer_cmd(struct smu_cmd_buf *cmd_buf)
+{
+       cmd_buf->cmd = 0x8e;
+       cmd_buf->length = 1;
+       cmd_buf->data[0] = 0x02;
+}
+#endif
+
+static inline void smu_fill_set_rtc_cmd(struct smu_cmd_buf *cmd_buf,
+                                       struct rtc_time *time)
+{
+       cmd_buf->cmd = 0x8e;
+       cmd_buf->length = 8;
+       cmd_buf->data[0] = 0x80;
+       cmd_buf->data[1] = hex2bcd(time->tm_sec);
+       cmd_buf->data[2] = hex2bcd(time->tm_min);
+       cmd_buf->data[3] = hex2bcd(time->tm_hour);
+       cmd_buf->data[4] = time->tm_wday;
+       cmd_buf->data[5] = hex2bcd(time->tm_mday);
+       cmd_buf->data[6] = hex2bcd(time->tm_mon) + 1;
+       cmd_buf->data[7] = hex2bcd(time->tm_year - 100);
+}
+
+static inline void smu_fill_get_rtc_cmd(struct smu_cmd_buf *cmd_buf)
+{
+       cmd_buf->cmd = 0x8e;
+       cmd_buf->length = 1;
+       cmd_buf->data[0] = 0x81;
+}
+
+static void smu_parse_get_rtc_reply(struct smu_cmd_buf *cmd_buf,
+                                   struct rtc_time *time)
+{
+       time->tm_sec = bcd2hex(cmd_buf->data[0]);
+       time->tm_min = bcd2hex(cmd_buf->data[1]);
+       time->tm_hour = bcd2hex(cmd_buf->data[2]);
+       time->tm_wday = bcd2hex(cmd_buf->data[3]);
+       time->tm_mday = bcd2hex(cmd_buf->data[4]);
+       time->tm_mon = bcd2hex(cmd_buf->data[5]) - 1;
+       time->tm_year = bcd2hex(cmd_buf->data[6]) + 100;
+}
+
+int smu_get_rtc_time(struct rtc_time *time)
+{
+       unsigned long flags;
+       int rc;
+
+       if (smu == NULL)
+               return -ENODEV;
+
+       memset(time, 0, sizeof(struct rtc_time));
+       spin_lock_irqsave(&smu->lock, flags);
+       smu_fill_get_rtc_cmd(smu->cmd_buf);
+       rc = smu_do_cmd(smu);
+       if (rc == 0)
+               smu_parse_get_rtc_reply(smu->cmd_buf, time);
+       spin_unlock_irqrestore(&smu->lock, flags);
+
+       return rc;
+}
+
+int smu_set_rtc_time(struct rtc_time *time)
+{
+       unsigned long flags;
+       int rc;
+
+       if (smu == NULL)
+               return -ENODEV;
+
+       spin_lock_irqsave(&smu->lock, flags);
+       smu_fill_set_rtc_cmd(smu->cmd_buf, time);
+       rc = smu_do_cmd(smu);
+       spin_unlock_irqrestore(&smu->lock, flags);
+
+       return rc;
+}
+
+void smu_shutdown(void)
+{
+       const unsigned char *command = "SHUTDOWN";
+       unsigned long flags;
+
+       if (smu == NULL)
+               return;
+
+       spin_lock_irqsave(&smu->lock, flags);
+       smu->cmd_buf->cmd = 0xaa;
+       smu->cmd_buf->length = strlen(command);
+       strcpy(smu->cmd_buf->data, command);
+       smu_do_cmd(smu);
+       for (;;)
+               ;
+       spin_unlock_irqrestore(&smu->lock, flags);
+}
+
+void smu_restart(void)
+{
+       const unsigned char *command = "RESTART";
+       unsigned long flags;
+
+       if (smu == NULL)
+               return;
+
+       spin_lock_irqsave(&smu->lock, flags);
+       smu->cmd_buf->cmd = 0xaa;
+       smu->cmd_buf->length = strlen(command);
+       strcpy(smu->cmd_buf->data, command);
+       smu_do_cmd(smu);
+       for (;;)
+               ;
+       spin_unlock_irqrestore(&smu->lock, flags);
+}
+
+int smu_present(void)
+{
+       return smu != NULL;
+}
+
+
+int smu_init (void)
+{
+       struct device_node *np;
+       u32 *data;
+
+        np = of_find_node_by_type(NULL, "smu");
+        if (np == NULL)
+               return -ENODEV;
+
+       if (smu_cmdbuf_abs == 0) {
+               printk(KERN_ERR "SMU: Command buffer not allocated !\n");
+               return -EINVAL;
+       }
+
+       smu = alloc_bootmem(sizeof(struct smu_device));
+       if (smu == NULL)
+               return -ENOMEM;
+       memset(smu, 0, sizeof(*smu));
+
+       spin_lock_init(&smu->lock);
+       smu->of_node = np;
+       /* smu_cmdbuf_abs is in the low 2G of RAM, can be converted to a
+        * 32 bits value safely
+        */
+       smu->cmd_buf_abs = (u32)smu_cmdbuf_abs;
+       smu->cmd_buf = (struct smu_cmd_buf *)abs_to_virt(smu_cmdbuf_abs);
+
+       np = of_find_node_by_name(NULL, "smu-doorbell");
+       if (np == NULL) {
+               printk(KERN_ERR "SMU: Can't find doorbell GPIO !\n");
+               goto fail;
+       }
+       data = (u32 *)get_property(np, "reg", NULL);
+       of_node_put(np);
+       if (data == NULL) {
+               printk(KERN_ERR "SMU: Can't find doorbell GPIO address !\n");
+               goto fail;
+       }
+
+       /* Current setup has one doorbell GPIO that does both doorbell
+        * and ack. GPIOs are at 0x50, best would be to find that out
+        * in the device-tree though.
+        */
+       smu->db_req = 0x50 + *data;
+       smu->db_ack = 0x50 + *data;
+
+       /* Doorbell buffer is currently hard-coded, I didn't find a proper
+        * device-tree entry giving the address. Best would probably to use
+        * an offset for K2 base though, but let's do it that way for now.
+        */
+       smu->db_buf = ioremap(0x8000860c, 0x1000);
+       if (smu->db_buf == NULL) {
+               printk(KERN_ERR "SMU: Can't map doorbell buffer pointer !\n");
+               goto fail;
+       }
+
+       sys_ctrler = SYS_CTRLER_SMU;
+       return 0;
+
+ fail:
+       smu = NULL;
+       return -ENXIO;
+
+}
diff --git a/drivers/md/dm-emc.c b/drivers/md/dm-emc.c
new file mode 100644 (file)
index 0000000..c706767
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2004 SUSE LINUX Products GmbH. All rights reserved.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is released under the GPL.
+ *
+ * Multipath support for EMC CLARiiON AX/CX-series hardware.
+ */
+
+#include "dm.h"
+#include "dm-hw-handler.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+
+struct emc_handler {
+       spinlock_t lock;
+
+       /* Whether we should send the short trespass command (FC-series)
+        * or the long version (default for AX/CX CLARiiON arrays). */
+       unsigned short_trespass;
+       /* Whether or not to honor SCSI reservations when initiating a
+        * switch-over. Default: Don't. */
+       unsigned hr;
+
+       unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+};
+
+#define TRESPASS_PAGE 0x22
+#define EMC_FAILOVER_TIMEOUT (60 * HZ)
+
+/* Code borrowed from dm-lsi-rdac by Mike Christie */
+
+static inline void free_bio(struct bio *bio)
+{
+       __free_page(bio->bi_io_vec[0].bv_page);
+       bio_put(bio);
+}
+
+static int emc_endio(struct bio *bio, unsigned int bytes_done, int error)
+{
+       struct path *path = bio->bi_private;
+
+       if (bio->bi_size)
+               return 1;
+
+       /* We also need to look at the sense keys here whether or not to
+        * switch to the next PG etc.
+        *
+        * For now simple logic: either it works or it doesn't.
+        */
+       if (error)
+               dm_pg_init_complete(path, MP_FAIL_PATH);
+       else
+               dm_pg_init_complete(path, 0);
+
+       /* request is freed in block layer */
+       free_bio(bio);
+
+       return 0;
+}
+
+static struct bio *get_failover_bio(struct path *path, unsigned data_size)
+{
+       struct bio *bio;
+       struct page *page;
+
+       bio = bio_alloc(GFP_ATOMIC, 1);
+       if (!bio) {
+               DMERR("dm-emc: get_failover_bio: bio_alloc() failed.");
+               return NULL;
+       }
+
+       bio->bi_rw |= (1 << BIO_RW);
+       bio->bi_bdev = path->dev->bdev;
+       bio->bi_sector = 0;
+       bio->bi_private = path;
+       bio->bi_end_io = emc_endio;
+
+       page = alloc_page(GFP_ATOMIC);
+       if (!page) {
+               DMERR("dm-emc: get_failover_bio: alloc_page() failed.");
+               bio_put(bio);
+               return NULL;
+       }
+
+       if (bio_add_page(bio, page, data_size, 0) != data_size) {
+               DMERR("dm-emc: get_failover_bio: alloc_page() failed.");
+               __free_page(page);
+               bio_put(bio);
+               return NULL;
+       }
+
+       return bio;
+}
+
+static struct request *get_failover_req(struct emc_handler *h,
+                                       struct bio *bio, struct path *path)
+{
+       struct request *rq;
+       struct block_device *bdev = bio->bi_bdev;
+       struct request_queue *q = bdev_get_queue(bdev);
+
+       /* FIXME: Figure out why it fails with GFP_ATOMIC. */
+       rq = blk_get_request(q, WRITE, __GFP_WAIT);
+       if (!rq) {
+               DMERR("dm-emc: get_failover_req: blk_get_request failed");
+               return NULL;
+       }
+
+       rq->bio = rq->biotail = bio;
+       blk_rq_bio_prep(q, rq, bio);
+
+       rq->rq_disk = bdev->bd_contains->bd_disk;
+
+       /* bio backed don't set data */
+       rq->buffer = rq->data = NULL;
+       /* rq data_len used for pc cmd's request_bufflen */
+       rq->data_len = bio->bi_size;
+
+       rq->sense = h->sense;
+       memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+       rq->sense_len = 0;
+
+       memset(&rq->cmd, 0, BLK_MAX_CDB);
+
+       rq->timeout = EMC_FAILOVER_TIMEOUT;
+       rq->flags |= (REQ_BLOCK_PC | REQ_FAILFAST | REQ_NOMERGE);
+
+       return rq;
+}
+
+static struct request *emc_trespass_get(struct emc_handler *h,
+                                       struct path *path)
+{
+       struct bio *bio;
+       struct request *rq;
+       unsigned char *page22;
+       unsigned char long_trespass_pg[] = {
+               0, 0, 0, 0,
+               TRESPASS_PAGE,        /* Page code */
+               0x09,                 /* Page length - 2 */
+               h->hr ? 0x01 : 0x81,  /* Trespass code + Honor reservation bit */
+               0xff, 0xff,           /* Trespass target */
+               0, 0, 0, 0, 0, 0      /* Reserved bytes / unknown */
+               };
+       unsigned char short_trespass_pg[] = {
+               0, 0, 0, 0,
+               TRESPASS_PAGE,        /* Page code */
+               0x02,                 /* Page length - 2 */
+               h->hr ? 0x01 : 0x81,  /* Trespass code + Honor reservation bit */
+               0xff,                 /* Trespass target */
+               };
+       unsigned data_size = h->short_trespass ? sizeof(short_trespass_pg) :
+                               sizeof(long_trespass_pg);
+
+       /* get bio backing */
+       if (data_size > PAGE_SIZE)
+               /* this should never happen */
+               return NULL;
+
+       bio = get_failover_bio(path, data_size);
+       if (!bio) {
+               DMERR("dm-emc: emc_trespass_get: no bio");
+               return NULL;
+       }
+
+       page22 = (unsigned char *)bio_data(bio);
+       memset(page22, 0, data_size);
+
+       memcpy(page22, h->short_trespass ?
+               short_trespass_pg : long_trespass_pg, data_size);
+
+       /* get request for block layer packet command */
+       rq = get_failover_req(h, bio, path);
+       if (!rq) {
+               DMERR("dm-emc: emc_trespass_get: no rq");
+               free_bio(bio);
+               return NULL;
+       }
+
+       /* Prepare the command. */
+       rq->cmd[0] = MODE_SELECT;
+       rq->cmd[1] = 0x10;
+       rq->cmd[4] = data_size;
+       rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
+
+       return rq;
+}
+
+static void emc_pg_init(struct hw_handler *hwh, unsigned bypassed,
+                       struct path *path)
+{
+       struct request *rq;
+       struct request_queue *q = bdev_get_queue(path->dev->bdev);
+
+       /*
+        * We can either blindly init the pg (then look at the sense),
+        * or we can send some commands to get the state here (then
+        * possibly send the fo cmnd), or we can also have the
+        * initial state passed into us and then get an update here.
+        */
+       if (!q) {
+               DMINFO("dm-emc: emc_pg_init: no queue");
+               goto fail_path;
+       }
+
+       /* FIXME: The request should be pre-allocated. */
+       rq = emc_trespass_get(hwh->context, path);
+       if (!rq) {
+               DMERR("dm-emc: emc_pg_init: no rq");
+               goto fail_path;
+       }
+
+       DMINFO("dm-emc: emc_pg_init: sending switch-over command");
+       elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1);
+       return;
+
+fail_path:
+       dm_pg_init_complete(path, MP_FAIL_PATH);
+}
+
+static struct emc_handler *alloc_emc_handler(void)
+{
+       struct emc_handler *h = kmalloc(sizeof(*h), GFP_KERNEL);
+
+       if (h) {
+               memset(h, 0, sizeof(*h));
+               spin_lock_init(&h->lock);
+       }
+
+       return h;
+}
+
+static int emc_create(struct hw_handler *hwh, unsigned argc, char **argv)
+{
+       struct emc_handler *h;
+       unsigned hr, short_trespass;
+
+       if (argc == 0) {
+               /* No arguments: use defaults */
+               hr = 0;
+               short_trespass = 0;
+       } else if (argc != 2) {
+               DMWARN("dm-emc hwhandler: incorrect number of arguments");
+               return -EINVAL;
+       } else {
+               if ((sscanf(argv[0], "%u", &short_trespass) != 1)
+                       || (short_trespass > 1)) {
+                       DMWARN("dm-emc: invalid trespass mode selected");
+                       return -EINVAL;
+               }
+
+               if ((sscanf(argv[1], "%u", &hr) != 1)
+                       || (hr > 1)) {
+                       DMWARN("dm-emc: invalid honor reservation flag selected");
+                       return -EINVAL;
+               }
+       }
+
+       h = alloc_emc_handler();
+       if (!h)
+               return -ENOMEM;
+
+       hwh->context = h;
+
+       if ((h->short_trespass = short_trespass))
+               DMWARN("dm-emc: short trespass command will be send");
+       else
+               DMWARN("dm-emc: long trespass command will be send");
+
+       if ((h->hr = hr))
+               DMWARN("dm-emc: honor reservation bit will be set");
+       else
+               DMWARN("dm-emc: honor reservation bit will not be set (default)");
+
+       return 0;
+}
+
+static void emc_destroy(struct hw_handler *hwh)
+{
+       struct emc_handler *h = (struct emc_handler *) hwh->context;
+
+       kfree(h);
+       hwh->context = NULL;
+}
+
+static unsigned emc_error(struct hw_handler *hwh, struct bio *bio)
+{
+       /* FIXME: Patch from axboe still missing */
+#if 0
+       int sense;
+
+       if (bio->bi_error & BIO_SENSE) {
+               sense = bio->bi_error & 0xffffff; /* sense key / asc / ascq */
+
+               if (sense == 0x020403) {
+                       /* LUN Not Ready - Manual Intervention Required
+                        * indicates this is a passive path.
+                        *
+                        * FIXME: However, if this is seen and EVPD C0
+                        * indicates that this is due to a NDU in
+                        * progress, we should set FAIL_PATH too.
+                        * This indicates we might have to do a SCSI
+                        * inquiry in the end_io path. Ugh. */
+                       return MP_BYPASS_PG | MP_RETRY_IO;
+               } else if (sense == 0x052501) {
+                       /* An array based copy is in progress. Do not
+                        * fail the path, do not bypass to another PG,
+                        * do not retry. Fail the IO immediately.
+                        * (Actually this is the same conclusion as in
+                        * the default handler, but lets make sure.) */
+                       return 0;
+               } else if (sense == 0x062900) {
+                       /* Unit Attention Code. This is the first IO
+                        * to the new path, so just retry. */
+                       return MP_RETRY_IO;
+               }
+       }
+#endif
+
+       /* Try default handler */
+       return dm_scsi_err_handler(hwh, bio);
+}
+
+static struct hw_handler_type emc_hwh = {
+       .name = "emc",
+       .module = THIS_MODULE,
+       .create = emc_create,
+       .destroy = emc_destroy,
+       .pg_init = emc_pg_init,
+       .error = emc_error,
+};
+
+static int __init dm_emc_init(void)
+{
+       int r = dm_register_hw_handler(&emc_hwh);
+
+       if (r < 0)
+               DMERR("emc: register failed %d", r);
+
+       DMINFO("dm-emc version 0.0.3 loaded");
+
+       return r;
+}
+
+static void __exit dm_emc_exit(void)
+{
+       int r = dm_unregister_hw_handler(&emc_hwh);
+
+       if (r < 0)
+               DMERR("emc: unregister failed %d", r);
+}
+
+module_init(dm_emc_init);
+module_exit(dm_emc_exit);
+
+MODULE_DESCRIPTION(DM_NAME " EMC CX/AX/FC-family multipath");
+MODULE_AUTHOR("Lars Marowsky-Bree <lmb@suse.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm-hw-handler.c b/drivers/md/dm-hw-handler.c
new file mode 100644 (file)
index 0000000..4cc0010
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is released under the GPL.
+ *
+ * Multipath hardware handler registration.
+ */
+
+#include "dm.h"
+#include "dm-hw-handler.h"
+
+#include <linux/slab.h>
+
+struct hwh_internal {
+       struct hw_handler_type hwht;
+
+       struct list_head list;
+       long use;
+};
+
+#define hwht_to_hwhi(__hwht) container_of((__hwht), struct hwh_internal, hwht)
+
+static LIST_HEAD(_hw_handlers);
+static DECLARE_RWSEM(_hwh_lock);
+
+static struct hwh_internal *__find_hw_handler_type(const char *name)
+{
+       struct hwh_internal *hwhi;
+
+       list_for_each_entry(hwhi, &_hw_handlers, list) {
+               if (!strcmp(name, hwhi->hwht.name))
+                       return hwhi;
+       }
+
+       return NULL;
+}
+
+static struct hwh_internal *get_hw_handler(const char *name)
+{
+       struct hwh_internal *hwhi;
+
+       down_read(&_hwh_lock);
+       hwhi = __find_hw_handler_type(name);
+       if (hwhi) {
+               if ((hwhi->use == 0) && !try_module_get(hwhi->hwht.module))
+                       hwhi = NULL;
+               else
+                       hwhi->use++;
+       }
+       up_read(&_hwh_lock);
+
+       return hwhi;
+}
+
+struct hw_handler_type *dm_get_hw_handler(const char *name)
+{
+       struct hwh_internal *hwhi;
+
+       if (!name)
+               return NULL;
+
+       hwhi = get_hw_handler(name);
+       if (!hwhi) {
+               request_module("dm-%s", name);
+               hwhi = get_hw_handler(name);
+       }
+
+       return hwhi ? &hwhi->hwht : NULL;
+}
+
+void dm_put_hw_handler(struct hw_handler_type *hwht)
+{
+       struct hwh_internal *hwhi;
+
+       if (!hwht)
+               return;
+
+       down_read(&_hwh_lock);
+       hwhi = __find_hw_handler_type(hwht->name);
+       if (!hwhi)
+               goto out;
+
+       if (--hwhi->use == 0)
+               module_put(hwhi->hwht.module);
+
+       if (hwhi->use < 0)
+               BUG();
+
+      out:
+       up_read(&_hwh_lock);
+}
+
+static struct hwh_internal *_alloc_hw_handler(struct hw_handler_type *hwht)
+{
+       struct hwh_internal *hwhi = kmalloc(sizeof(*hwhi), GFP_KERNEL);
+
+       if (hwhi) {
+               memset(hwhi, 0, sizeof(*hwhi));
+               hwhi->hwht = *hwht;
+       }
+
+       return hwhi;
+}
+
+int dm_register_hw_handler(struct hw_handler_type *hwht)
+{
+       int r = 0;
+       struct hwh_internal *hwhi = _alloc_hw_handler(hwht);
+
+       if (!hwhi)
+               return -ENOMEM;
+
+       down_write(&_hwh_lock);
+
+       if (__find_hw_handler_type(hwht->name)) {
+               kfree(hwhi);
+               r = -EEXIST;
+       } else
+               list_add(&hwhi->list, &_hw_handlers);
+
+       up_write(&_hwh_lock);
+
+       return r;
+}
+
+int dm_unregister_hw_handler(struct hw_handler_type *hwht)
+{
+       struct hwh_internal *hwhi;
+
+       down_write(&_hwh_lock);
+
+       hwhi = __find_hw_handler_type(hwht->name);
+       if (!hwhi) {
+               up_write(&_hwh_lock);
+               return -EINVAL;
+       }
+
+       if (hwhi->use) {
+               up_write(&_hwh_lock);
+               return -ETXTBSY;
+       }
+
+       list_del(&hwhi->list);
+
+       up_write(&_hwh_lock);
+
+       kfree(hwhi);
+
+       return 0;
+}
+
+unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio)
+{
+#if 0
+       int sense_key, asc, ascq;
+
+       if (bio->bi_error & BIO_SENSE) {
+               /* FIXME: This is just an initial guess. */
+               /* key / asc / ascq */
+               sense_key = (bio->bi_error >> 16) & 0xff;
+               asc = (bio->bi_error >> 8) & 0xff;
+               ascq = bio->bi_error & 0xff;
+
+               switch (sense_key) {
+                       /* This block as a whole comes from the device.
+                        * So no point retrying on another path. */
+               case 0x03:      /* Medium error */
+               case 0x05:      /* Illegal request */
+               case 0x07:      /* Data protect */
+               case 0x08:      /* Blank check */
+               case 0x0a:      /* copy aborted */
+               case 0x0c:      /* obsolete - no clue ;-) */
+               case 0x0d:      /* volume overflow */
+               case 0x0e:      /* data miscompare */
+               case 0x0f:      /* reserved - no idea either. */
+                       return MP_ERROR_IO;
+
+                       /* For these errors it's unclear whether they
+                        * come from the device or the controller.
+                        * So just lets try a different path, and if
+                        * it eventually succeeds, user-space will clear
+                        * the paths again... */
+               case 0x02:      /* Not ready */
+               case 0x04:      /* Hardware error */
+               case 0x09:      /* vendor specific */
+               case 0x0b:      /* Aborted command */
+                       return MP_FAIL_PATH;
+
+               case 0x06:      /* Unit attention - might want to decode */
+                       if (asc == 0x04 && ascq == 0x01)
+                               /* "Unit in the process of
+                                * becoming ready" */
+                               return 0;
+                       return MP_FAIL_PATH;
+
+                       /* FIXME: For Unit Not Ready we may want
+                        * to have a generic pg activation
+                        * feature (START_UNIT). */
+
+                       /* Should these two ever end up in the
+                        * error path? I don't think so. */
+               case 0x00:      /* No sense */
+               case 0x01:      /* Recovered error */
+                       return 0;
+               }
+       }
+#endif
+
+       /* We got no idea how to decode the other kinds of errors ->
+        * assume generic error condition. */
+       return MP_FAIL_PATH;
+}
+
+EXPORT_SYMBOL_GPL(dm_register_hw_handler);
+EXPORT_SYMBOL_GPL(dm_unregister_hw_handler);
+EXPORT_SYMBOL_GPL(dm_scsi_err_handler);
diff --git a/drivers/md/dm-hw-handler.h b/drivers/md/dm-hw-handler.h
new file mode 100644 (file)
index 0000000..15f5629
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is released under the GPL.
+ *
+ * Multipath hardware handler registration.
+ */
+
+#ifndef        DM_HW_HANDLER_H
+#define        DM_HW_HANDLER_H
+
+#include <linux/device-mapper.h>
+
+#include "dm-mpath.h"
+
+struct hw_handler_type;
+struct hw_handler {
+       struct hw_handler_type *type;
+       void *context;
+};
+
+/*
+ * Constructs a hardware handler object, takes custom arguments
+ */
+/* Information about a hardware handler type */
+struct hw_handler_type {
+       char *name;
+       struct module *module;
+
+       int (*create) (struct hw_handler *handler, unsigned int argc,
+                      char **argv);
+       void (*destroy) (struct hw_handler *hwh);
+
+       void (*pg_init) (struct hw_handler *hwh, unsigned bypassed,
+                        struct path *path);
+       unsigned (*error) (struct hw_handler *hwh, struct bio *bio);
+       int (*status) (struct hw_handler *hwh, status_type_t type,
+                      char *result, unsigned int maxlen);
+};
+
+/* Register a hardware handler */
+int dm_register_hw_handler(struct hw_handler_type *type);
+
+/* Unregister a hardware handler */
+int dm_unregister_hw_handler(struct hw_handler_type *type);
+
+/* Returns a registered hardware handler type */
+struct hw_handler_type *dm_get_hw_handler(const char *name);
+
+/* Releases a hardware handler  */
+void dm_put_hw_handler(struct hw_handler_type *hwht);
+
+/* Default err function */
+unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio);
+
+/* Error flags for err and dm_pg_init_complete */
+#define MP_FAIL_PATH 1
+#define MP_BYPASS_PG 2
+#define MP_ERROR_IO  4 /* Don't retry this I/O */
+
+#endif
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
new file mode 100644 (file)
index 0000000..0c1b852
--- /dev/null
@@ -0,0 +1,1317 @@
+/*
+ * Copyright (C) 2003 Sistina Software Limited.
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+#include "dm-path-selector.h"
+#include "dm-hw-handler.h"
+#include "dm-bio-list.h"
+#include "dm-bio-record.h"
+
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/mempool.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/workqueue.h>
+#include <asm/atomic.h>
+
+#define MESG_STR(x) x, sizeof(x)
+
+/* Path properties */
+struct pgpath {
+       struct list_head list;
+
+       struct priority_group *pg;      /* Owning PG */
+       unsigned fail_count;            /* Cumulative failure count */
+
+       struct path path;
+};
+
+#define path_to_pgpath(__pgp) container_of((__pgp), struct pgpath, path)
+
+/*
+ * Paths are grouped into Priority Groups and numbered from 1 upwards.
+ * Each has a path selector which controls which path gets used.
+ */
+struct priority_group {
+       struct list_head list;
+
+       struct multipath *m;            /* Owning multipath instance */
+       struct path_selector ps;
+
+       unsigned pg_num;                /* Reference number */
+       unsigned bypassed;              /* Temporarily bypass this PG? */
+
+       unsigned nr_pgpaths;            /* Number of paths in PG */
+       struct list_head pgpaths;
+};
+
+/* Multipath context */
+struct multipath {
+       struct list_head list;
+       struct dm_target *ti;
+
+       spinlock_t lock;
+
+       struct hw_handler hw_handler;
+       unsigned nr_priority_groups;
+       struct list_head priority_groups;
+       unsigned pg_init_required;      /* pg_init needs calling? */
+
+       unsigned nr_valid_paths;        /* Total number of usable paths */
+       struct pgpath *current_pgpath;
+       struct priority_group *current_pg;
+       struct priority_group *next_pg; /* Switch to this PG if set */
+       unsigned repeat_count;          /* I/Os left before calling PS again */
+
+       unsigned queue_io;              /* Must we queue all I/O? */
+       unsigned queue_if_no_path;      /* Queue I/O if last path fails? */
+       unsigned suspended;             /* Has dm core suspended our I/O? */
+
+       struct work_struct process_queued_ios;
+       struct bio_list queued_ios;
+       unsigned queue_size;
+
+       struct work_struct trigger_event;
+
+       /*
+        * We must use a mempool of mpath_io structs so that we
+        * can resubmit bios on error.
+        */
+       mempool_t *mpio_pool;
+};
+
+/*
+ * Context information attached to each bio we process.
+ */
+struct mpath_io {
+       struct pgpath *pgpath;
+       struct dm_bio_details details;
+};
+
+typedef int (*action_fn) (struct pgpath *pgpath);
+
+#define MIN_IOS 256    /* Mempool size */
+
+static kmem_cache_t *_mpio_cache;
+
+struct workqueue_struct *kmultipathd;
+static void process_queued_ios(void *data);
+static void trigger_event(void *data);
+
+
+/*-----------------------------------------------
+ * Allocation routines
+ *-----------------------------------------------*/
+
+static struct pgpath *alloc_pgpath(void)
+{
+       struct pgpath *pgpath = kmalloc(sizeof(*pgpath), GFP_KERNEL);
+
+       if (pgpath) {
+               memset(pgpath, 0, sizeof(*pgpath));
+               pgpath->path.is_active = 1;
+       }
+
+       return pgpath;
+}
+
+static inline void free_pgpath(struct pgpath *pgpath)
+{
+       kfree(pgpath);
+}
+
+static struct priority_group *alloc_priority_group(void)
+{
+       struct priority_group *pg;
+
+       pg = kmalloc(sizeof(*pg), GFP_KERNEL);
+       if (!pg)
+               return NULL;
+
+       memset(pg, 0, sizeof(*pg));
+       INIT_LIST_HEAD(&pg->pgpaths);
+
+       return pg;
+}
+
+static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti)
+{
+       struct pgpath *pgpath, *tmp;
+
+       list_for_each_entry_safe(pgpath, tmp, pgpaths, list) {
+               list_del(&pgpath->list);
+               dm_put_device(ti, pgpath->path.dev);
+               free_pgpath(pgpath);
+       }
+}
+
+static void free_priority_group(struct priority_group *pg,
+                               struct dm_target *ti)
+{
+       struct path_selector *ps = &pg->ps;
+
+       if (ps->type) {
+               ps->type->destroy(ps);
+               dm_put_path_selector(ps->type);
+       }
+
+       free_pgpaths(&pg->pgpaths, ti);
+       kfree(pg);
+}
+
+static struct multipath *alloc_multipath(void)
+{
+       struct multipath *m;
+
+       m = kmalloc(sizeof(*m), GFP_KERNEL);
+       if (m) {
+               memset(m, 0, sizeof(*m));
+               INIT_LIST_HEAD(&m->priority_groups);
+               spin_lock_init(&m->lock);
+               m->queue_io = 1;
+               INIT_WORK(&m->process_queued_ios, process_queued_ios, m);
+               INIT_WORK(&m->trigger_event, trigger_event, m);
+               m->mpio_pool = mempool_create(MIN_IOS, mempool_alloc_slab,
+                                             mempool_free_slab, _mpio_cache);
+               if (!m->mpio_pool) {
+                       kfree(m);
+                       return NULL;
+               }
+       }
+
+       return m;
+}
+
+static void free_multipath(struct multipath *m)
+{
+       struct priority_group *pg, *tmp;
+       struct hw_handler *hwh = &m->hw_handler;
+
+       list_for_each_entry_safe(pg, tmp, &m->priority_groups, list) {
+               list_del(&pg->list);
+               free_priority_group(pg, m->ti);
+       }
+
+       if (hwh->type) {
+               hwh->type->destroy(hwh);
+               dm_put_hw_handler(hwh->type);
+       }
+
+       mempool_destroy(m->mpio_pool);
+       kfree(m);
+}
+
+
+/*-----------------------------------------------
+ * Path selection
+ *-----------------------------------------------*/
+
+static void __switch_pg(struct multipath *m, struct pgpath *pgpath)
+{
+       struct hw_handler *hwh = &m->hw_handler;
+
+       m->current_pg = pgpath->pg;
+
+       /* Must we initialise the PG first, and queue I/O till it's ready? */
+       if (hwh->type && hwh->type->pg_init) {
+               m->pg_init_required = 1;
+               m->queue_io = 1;
+       } else {
+               m->pg_init_required = 0;
+               m->queue_io = 0;
+       }
+}
+
+static int __choose_path_in_pg(struct multipath *m, struct priority_group *pg)
+{
+       struct path *path;
+
+       path = pg->ps.type->select_path(&pg->ps, &m->repeat_count);
+       if (!path)
+               return -ENXIO;
+
+       m->current_pgpath = path_to_pgpath(path);
+
+       if (m->current_pg != pg)
+               __switch_pg(m, m->current_pgpath);
+
+       return 0;
+}
+
+static void __choose_pgpath(struct multipath *m)
+{
+       struct priority_group *pg;
+       unsigned bypassed = 1;
+
+       if (!m->nr_valid_paths)
+               goto failed;
+
+       /* Were we instructed to switch PG? */
+       if (m->next_pg) {
+               pg = m->next_pg;
+               m->next_pg = NULL;
+               if (!__choose_path_in_pg(m, pg))
+                       return;
+       }
+
+       /* Don't change PG until it has no remaining paths */
+       if (m->current_pg && !__choose_path_in_pg(m, m->current_pg))
+               return;
+
+       /*
+        * Loop through priority groups until we find a valid path.
+        * First time we skip PGs marked 'bypassed'.
+        * Second time we only try the ones we skipped.
+        */
+       do {
+               list_for_each_entry(pg, &m->priority_groups, list) {
+                       if (pg->bypassed == bypassed)
+                               continue;
+                       if (!__choose_path_in_pg(m, pg))
+                               return;
+               }
+       } while (bypassed--);
+
+failed:
+       m->current_pgpath = NULL;
+       m->current_pg = NULL;
+}
+
+static int map_io(struct multipath *m, struct bio *bio, struct mpath_io *mpio,
+                 unsigned was_queued)
+{
+       int r = 1;
+       unsigned long flags;
+       struct pgpath *pgpath;
+
+       spin_lock_irqsave(&m->lock, flags);
+
+       /* Do we need to select a new pgpath? */
+       if (!m->current_pgpath ||
+           (!m->queue_io && (m->repeat_count && --m->repeat_count == 0)))
+               __choose_pgpath(m);
+
+       pgpath = m->current_pgpath;
+
+       if (was_queued)
+               m->queue_size--;
+
+       if ((pgpath && m->queue_io) ||
+           (!pgpath && m->queue_if_no_path && !m->suspended)) {
+               /* Queue for the daemon to resubmit */
+               bio_list_add(&m->queued_ios, bio);
+               m->queue_size++;
+               if (m->pg_init_required || !m->queue_io)
+                       queue_work(kmultipathd, &m->process_queued_ios);
+               pgpath = NULL;
+               r = 0;
+       } else if (!pgpath)
+               r = -EIO;               /* Failed */
+       else
+               bio->bi_bdev = pgpath->path.dev->bdev;
+
+       mpio->pgpath = pgpath;
+
+       spin_unlock_irqrestore(&m->lock, flags);
+
+       return r;
+}
+
+/*
+ * If we run out of usable paths, should we queue I/O or error it?
+ */
+static int queue_if_no_path(struct multipath *m, unsigned queue_if_no_path)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&m->lock, flags);
+
+       m->queue_if_no_path = queue_if_no_path;
+       if (!m->queue_if_no_path)
+               queue_work(kmultipathd, &m->process_queued_ios);
+
+       spin_unlock_irqrestore(&m->lock, flags);
+
+       return 0;
+}
+
+/*-----------------------------------------------------------------
+ * The multipath daemon is responsible for resubmitting queued ios.
+ *---------------------------------------------------------------*/
+
+static void dispatch_queued_ios(struct multipath *m)
+{
+       int r;
+       unsigned long flags;
+       struct bio *bio = NULL, *next;
+       struct mpath_io *mpio;
+       union map_info *info;
+
+       spin_lock_irqsave(&m->lock, flags);
+       bio = bio_list_get(&m->queued_ios);
+       spin_unlock_irqrestore(&m->lock, flags);
+
+       while (bio) {
+               next = bio->bi_next;
+               bio->bi_next = NULL;
+
+               info = dm_get_mapinfo(bio);
+               mpio = info->ptr;
+
+               r = map_io(m, bio, mpio, 1);
+               if (r < 0)
+                       bio_endio(bio, bio->bi_size, r);
+               else if (r == 1)
+                       generic_make_request(bio);
+
+               bio = next;
+       }
+}
+
+static void process_queued_ios(void *data)
+{
+       struct multipath *m = (struct multipath *) data;
+       struct hw_handler *hwh = &m->hw_handler;
+       struct pgpath *pgpath;
+       unsigned init_required, must_queue = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&m->lock, flags);
+
+       if (!m->current_pgpath)
+               __choose_pgpath(m);
+
+       pgpath = m->current_pgpath;
+
+       if ((pgpath && m->queue_io) ||
+           (!pgpath && m->queue_if_no_path && !m->suspended))
+               must_queue = 1;
+
+       init_required = m->pg_init_required;
+       if (init_required)
+               m->pg_init_required = 0;
+
+       spin_unlock_irqrestore(&m->lock, flags);
+
+       if (init_required)
+               hwh->type->pg_init(hwh, pgpath->pg->bypassed, &pgpath->path);
+
+       if (!must_queue)
+               dispatch_queued_ios(m);
+}
+
+/*
+ * An event is triggered whenever a path is taken out of use.
+ * Includes path failure and PG bypass.
+ */
+static void trigger_event(void *data)
+{
+       struct multipath *m = (struct multipath *) data;
+
+       dm_table_event(m->ti->table);
+}
+
+/*-----------------------------------------------------------------
+ * Constructor/argument parsing:
+ * <#multipath feature args> [<arg>]*
+ * <#hw_handler args> [hw_handler [<arg>]*]
+ * <#priority groups>
+ * <initial priority group>
+ *     [<selector> <#selector args> [<arg>]*
+ *      <#paths> <#per-path selector args>
+ *         [<path> [<arg>]* ]+ ]+
+ *---------------------------------------------------------------*/
+struct param {
+       unsigned min;
+       unsigned max;
+       char *error;
+};
+
+#define ESTR(s) ("dm-multipath: " s)
+
+static int read_param(struct param *param, char *str, unsigned *v, char **error)
+{
+       if (!str ||
+           (sscanf(str, "%u", v) != 1) ||
+           (*v < param->min) ||
+           (*v > param->max)) {
+               *error = param->error;
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+struct arg_set {
+       unsigned argc;
+       char **argv;
+};
+
+static char *shift(struct arg_set *as)
+{
+       char *r;
+
+       if (as->argc) {
+               as->argc--;
+               r = *as->argv;
+               as->argv++;
+               return r;
+       }
+
+       return NULL;
+}
+
+static void consume(struct arg_set *as, unsigned n)
+{
+       BUG_ON (as->argc < n);
+       as->argc -= n;
+       as->argv += n;
+}
+
+static int parse_path_selector(struct arg_set *as, struct priority_group *pg,
+                              struct dm_target *ti)
+{
+       int r;
+       struct path_selector_type *pst;
+       unsigned ps_argc;
+
+       static struct param _params[] = {
+               {0, 1024, ESTR("invalid number of path selector args")},
+       };
+
+       pst = dm_get_path_selector(shift(as));
+       if (!pst) {
+               ti->error = ESTR("unknown path selector type");
+               return -EINVAL;
+       }
+
+       r = read_param(_params, shift(as), &ps_argc, &ti->error);
+       if (r)
+               return -EINVAL;
+
+       r = pst->create(&pg->ps, ps_argc, as->argv);
+       if (r) {
+               dm_put_path_selector(pst);
+               ti->error = ESTR("path selector constructor failed");
+               return r;
+       }
+
+       pg->ps.type = pst;
+       consume(as, ps_argc);
+
+       return 0;
+}
+
+static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
+                              struct dm_target *ti)
+{
+       int r;
+       struct pgpath *p;
+
+       /* we need at least a path arg */
+       if (as->argc < 1) {
+               ti->error = ESTR("no device given");
+               return NULL;
+       }
+
+       p = alloc_pgpath();
+       if (!p)
+               return NULL;
+
+       r = dm_get_device(ti, shift(as), ti->begin, ti->len,
+                         dm_table_get_mode(ti->table), &p->path.dev);
+       if (r) {
+               ti->error = ESTR("error getting device");
+               goto bad;
+       }
+
+       r = ps->type->add_path(ps, &p->path, as->argc, as->argv, &ti->error);
+       if (r) {
+               dm_put_device(ti, p->path.dev);
+               goto bad;
+       }
+
+       return p;
+
+ bad:
+       free_pgpath(p);
+       return NULL;
+}
+
+static struct priority_group *parse_priority_group(struct arg_set *as,
+                                                  struct multipath *m,
+                                                  struct dm_target *ti)
+{
+       static struct param _params[] = {
+               {1, 1024, ESTR("invalid number of paths")},
+               {0, 1024, ESTR("invalid number of selector args")}
+       };
+
+       int r;
+       unsigned i, nr_selector_args, nr_params;
+       struct priority_group *pg;
+
+       if (as->argc < 2) {
+               as->argc = 0;
+               ti->error = ESTR("not enough priority group aruments");
+               return NULL;
+       }
+
+       pg = alloc_priority_group();
+       if (!pg) {
+               ti->error = ESTR("couldn't allocate priority group");
+               return NULL;
+       }
+       pg->m = m;
+
+       r = parse_path_selector(as, pg, ti);
+       if (r)
+               goto bad;
+
+       /*
+        * read the paths
+        */
+       r = read_param(_params, shift(as), &pg->nr_pgpaths, &ti->error);
+       if (r)
+               goto bad;
+
+       r = read_param(_params + 1, shift(as), &nr_selector_args, &ti->error);
+       if (r)
+               goto bad;
+
+       nr_params = 1 + nr_selector_args;
+       for (i = 0; i < pg->nr_pgpaths; i++) {
+               struct pgpath *pgpath;
+               struct arg_set path_args;
+
+               if (as->argc < nr_params)
+                       goto bad;
+
+               path_args.argc = nr_params;
+               path_args.argv = as->argv;
+
+               pgpath = parse_path(&path_args, &pg->ps, ti);
+               if (!pgpath)
+                       goto bad;
+
+               pgpath->pg = pg;
+               list_add_tail(&pgpath->list, &pg->pgpaths);
+               consume(as, nr_params);
+       }
+
+       return pg;
+
+ bad:
+       free_priority_group(pg, ti);
+       return NULL;
+}
+
+static int parse_hw_handler(struct arg_set *as, struct multipath *m,
+                           struct dm_target *ti)
+{
+       int r;
+       struct hw_handler_type *hwht;
+       unsigned hw_argc;
+
+       static struct param _params[] = {
+               {0, 1024, ESTR("invalid number of hardware handler args")},
+       };
+
+       r = read_param(_params, shift(as), &hw_argc, &ti->error);
+       if (r)
+               return -EINVAL;
+
+       if (!hw_argc)
+               return 0;
+
+       hwht = dm_get_hw_handler(shift(as));
+       if (!hwht) {
+               ti->error = ESTR("unknown hardware handler type");
+               return -EINVAL;
+       }
+
+       r = hwht->create(&m->hw_handler, hw_argc - 1, as->argv);
+       if (r) {
+               dm_put_hw_handler(hwht);
+               ti->error = ESTR("hardware handler constructor failed");
+               return r;
+       }
+
+       m->hw_handler.type = hwht;
+       consume(as, hw_argc - 1);
+
+       return 0;
+}
+
+static int parse_features(struct arg_set *as, struct multipath *m,
+                         struct dm_target *ti)
+{
+       int r;
+       unsigned argc;
+
+       static struct param _params[] = {
+               {0, 1, ESTR("invalid number of feature args")},
+       };
+
+       r = read_param(_params, shift(as), &argc, &ti->error);
+       if (r)
+               return -EINVAL;
+
+       if (!argc)
+               return 0;
+
+       if (!strnicmp(shift(as), MESG_STR("queue_if_no_path")))
+               return queue_if_no_path(m, 1);
+       else {
+               ti->error = "Unrecognised multipath feature request";
+               return -EINVAL;
+       }
+}
+
+static int multipath_ctr(struct dm_target *ti, unsigned int argc,
+                        char **argv)
+{
+       /* target parameters */
+       static struct param _params[] = {
+               {1, 1024, ESTR("invalid number of priority groups")},
+               {1, 1024, ESTR("invalid initial priority group number")},
+       };
+
+       int r;
+       struct multipath *m;
+       struct arg_set as;
+       unsigned pg_count = 0;
+       unsigned next_pg_num;
+
+       as.argc = argc;
+       as.argv = argv;
+
+       m = alloc_multipath();
+       if (!m) {
+               ti->error = ESTR("can't allocate multipath");
+               return -EINVAL;
+       }
+
+       r = parse_features(&as, m, ti);
+       if (r)
+               goto bad;
+
+       r = parse_hw_handler(&as, m, ti);
+       if (r)
+               goto bad;
+
+       r = read_param(_params, shift(&as), &m->nr_priority_groups, &ti->error);
+       if (r)
+               goto bad;
+
+       r = read_param(_params + 1, shift(&as), &next_pg_num, &ti->error);
+       if (r)
+               goto bad;
+
+       /* parse the priority groups */
+       while (as.argc) {
+               struct priority_group *pg;
+
+               pg = parse_priority_group(&as, m, ti);
+               if (!pg) {
+                       r = -EINVAL;
+                       goto bad;
+               }
+
+               m->nr_valid_paths += pg->nr_pgpaths;
+               list_add_tail(&pg->list, &m->priority_groups);
+               pg_count++;
+               pg->pg_num = pg_count;
+               if (!--next_pg_num)
+                       m->next_pg = pg;
+       }
+
+       if (pg_count != m->nr_priority_groups) {
+               ti->error = ESTR("priority group count mismatch");
+               r = -EINVAL;
+               goto bad;
+       }
+
+       ti->private = m;
+       m->ti = ti;
+
+       return 0;
+
+ bad:
+       free_multipath(m);
+       return r;
+}
+
+static void multipath_dtr(struct dm_target *ti)
+{
+       struct multipath *m = (struct multipath *) ti->private;
+       free_multipath(m);
+}
+
+/*
+ * Map bios, recording original fields for later in case we have to resubmit
+ */
+static int multipath_map(struct dm_target *ti, struct bio *bio,
+                        union map_info *map_context)
+{
+       int r;
+       struct mpath_io *mpio;
+       struct multipath *m = (struct multipath *) ti->private;
+
+       mpio = mempool_alloc(m->mpio_pool, GFP_NOIO);
+       dm_bio_record(&mpio->details, bio);
+
+       map_context->ptr = mpio;
+       bio->bi_rw |= (1 << BIO_RW_FAILFAST);
+       r = map_io(m, bio, mpio, 0);
+       if (r < 0)
+               mempool_free(mpio, m->mpio_pool);
+
+       return r;
+}
+
+/*
+ * Take a path out of use.
+ */
+static int fail_path(struct pgpath *pgpath)
+{
+       unsigned long flags;
+       struct multipath *m = pgpath->pg->m;
+
+       spin_lock_irqsave(&m->lock, flags);
+
+       if (!pgpath->path.is_active)
+               goto out;
+
+       DMWARN("dm-multipath: Failing path %s.", pgpath->path.dev->name);
+
+       pgpath->pg->ps.type->fail_path(&pgpath->pg->ps, &pgpath->path);
+       pgpath->path.is_active = 0;
+       pgpath->fail_count++;
+
+       m->nr_valid_paths--;
+
+       if (pgpath == m->current_pgpath)
+               m->current_pgpath = NULL;
+
+       queue_work(kmultipathd, &m->trigger_event);
+
+out:
+       spin_unlock_irqrestore(&m->lock, flags);
+
+       return 0;
+}
+
+/*
+ * Reinstate a previously-failed path
+ */
+static int reinstate_path(struct pgpath *pgpath)
+{
+       int r = 0;
+       unsigned long flags;
+       struct multipath *m = pgpath->pg->m;
+
+       spin_lock_irqsave(&m->lock, flags);
+
+       if (pgpath->path.is_active)
+               goto out;
+
+       if (!pgpath->pg->ps.type) {
+               DMWARN("Reinstate path not supported by path selector %s",
+                      pgpath->pg->ps.type->name);
+               r = -EINVAL;
+               goto out;
+       }
+
+       r = pgpath->pg->ps.type->reinstate_path(&pgpath->pg->ps, &pgpath->path);
+       if (r)
+               goto out;
+
+       pgpath->path.is_active = 1;
+
+       m->current_pgpath = NULL;
+       if (!m->nr_valid_paths++)
+               queue_work(kmultipathd, &m->process_queued_ios);
+
+       queue_work(kmultipathd, &m->trigger_event);
+
+out:
+       spin_unlock_irqrestore(&m->lock, flags);
+
+       return r;
+}
+
+/*
+ * Fail or reinstate all paths that match the provided struct dm_dev.
+ */
+static int action_dev(struct multipath *m, struct dm_dev *dev,
+                     action_fn action)
+{
+       int r = 0;
+       struct pgpath *pgpath;
+       struct priority_group *pg;
+
+       list_for_each_entry(pg, &m->priority_groups, list) {
+               list_for_each_entry(pgpath, &pg->pgpaths, list) {
+                       if (pgpath->path.dev == dev)
+                               r = action(pgpath);
+               }
+       }
+
+       return r;
+}
+
+/*
+ * Temporarily try to avoid having to use the specified PG
+ */
+static void bypass_pg(struct multipath *m, struct priority_group *pg,
+                     int bypassed)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&m->lock, flags);
+
+       pg->bypassed = bypassed;
+       m->current_pgpath = NULL;
+       m->current_pg = NULL;
+
+       spin_unlock_irqrestore(&m->lock, flags);
+
+       queue_work(kmultipathd, &m->trigger_event);
+}
+
+/*
+ * Switch to using the specified PG from the next I/O that gets mapped
+ */
+static int switch_pg_num(struct multipath *m, const char *pgstr)
+{
+       struct priority_group *pg;
+       unsigned pgnum;
+       unsigned long flags;
+
+       if (!pgstr || (sscanf(pgstr, "%u", &pgnum) != 1) || !pgnum ||
+           (pgnum > m->nr_priority_groups)) {
+               DMWARN("invalid PG number supplied to switch_pg_num");
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&m->lock, flags);
+       list_for_each_entry(pg, &m->priority_groups, list) {
+               pg->bypassed = 0;
+               if (--pgnum)
+                       continue;
+
+               m->current_pgpath = NULL;
+               m->current_pg = NULL;
+               m->next_pg = pg;
+       }
+       spin_unlock_irqrestore(&m->lock, flags);
+
+       queue_work(kmultipathd, &m->trigger_event);
+       return 0;
+}
+
+/*
+ * Set/clear bypassed status of a PG.
+ * PGs are numbered upwards from 1 in the order they were declared.
+ */
+static int bypass_pg_num(struct multipath *m, const char *pgstr, int bypassed)
+{
+       struct priority_group *pg;
+       unsigned pgnum;
+
+       if (!pgstr || (sscanf(pgstr, "%u", &pgnum) != 1) || !pgnum ||
+           (pgnum > m->nr_priority_groups)) {
+               DMWARN("invalid PG number supplied to bypass_pg");
+               return -EINVAL;
+       }
+
+       list_for_each_entry(pg, &m->priority_groups, list) {
+               if (!--pgnum)
+                       break;
+       }
+
+       bypass_pg(m, pg, bypassed);
+       return 0;
+}
+
+/*
+ * pg_init must call this when it has completed its initialisation
+ */
+void dm_pg_init_complete(struct path *path, unsigned err_flags)
+{
+       struct pgpath *pgpath = path_to_pgpath(path);
+       struct priority_group *pg = pgpath->pg;
+       struct multipath *m = pg->m;
+       unsigned long flags;
+
+       /* We insist on failing the path if the PG is already bypassed. */
+       if (err_flags && pg->bypassed)
+               err_flags |= MP_FAIL_PATH;
+
+       if (err_flags & MP_FAIL_PATH)
+               fail_path(pgpath);
+
+       if (err_flags & MP_BYPASS_PG)
+               bypass_pg(m, pg, 1);
+
+       spin_lock_irqsave(&m->lock, flags);
+       if (!err_flags)
+               m->queue_io = 0;
+       else {
+               m->current_pgpath = NULL;
+               m->current_pg = NULL;
+       }
+       queue_work(kmultipathd, &m->process_queued_ios);
+       spin_unlock_irqrestore(&m->lock, flags);
+}
+
+/*
+ * end_io handling
+ */
+static int do_end_io(struct multipath *m, struct bio *bio,
+                    int error, struct mpath_io *mpio)
+{
+       struct hw_handler *hwh = &m->hw_handler;
+       unsigned err_flags = MP_FAIL_PATH;      /* Default behavior */
+
+       if (!error)
+               return 0;       /* I/O complete */
+
+       if ((error == -EWOULDBLOCK) && bio_rw_ahead(bio))
+               return error;
+
+       spin_lock(&m->lock);
+       if (!m->nr_valid_paths) {
+               if (!m->queue_if_no_path || m->suspended) {
+                       spin_unlock(&m->lock);
+                       return -EIO;
+               } else {
+                       spin_unlock(&m->lock);
+                       goto requeue;
+               }
+       }
+       spin_unlock(&m->lock);
+
+       if (hwh->type && hwh->type->error)
+               err_flags = hwh->type->error(hwh, bio);
+
+       if (mpio->pgpath) {
+               if (err_flags & MP_FAIL_PATH)
+                       fail_path(mpio->pgpath);
+
+               if (err_flags & MP_BYPASS_PG)
+                       bypass_pg(m, mpio->pgpath->pg, 1);
+       }
+
+       if (err_flags & MP_ERROR_IO)
+               return -EIO;
+
+      requeue:
+       dm_bio_restore(&mpio->details, bio);
+
+       /* queue for the daemon to resubmit or fail */
+       spin_lock(&m->lock);
+       bio_list_add(&m->queued_ios, bio);
+       m->queue_size++;
+       if (!m->queue_io)
+               queue_work(kmultipathd, &m->process_queued_ios);
+       spin_unlock(&m->lock);
+
+       return 1;       /* io not complete */
+}
+
+static int multipath_end_io(struct dm_target *ti, struct bio *bio,
+                           int error, union map_info *map_context)
+{
+       struct multipath *m = (struct multipath *) ti->private;
+       struct mpath_io *mpio = (struct mpath_io *) map_context->ptr;
+       struct pgpath *pgpath = mpio->pgpath;
+       struct path_selector *ps;
+       int r;
+
+       r  = do_end_io(m, bio, error, mpio);
+       if (pgpath) {
+               ps = &pgpath->pg->ps;
+               if (ps->type->end_io)
+                       ps->type->end_io(ps, &pgpath->path);
+       }
+       if (r <= 0)
+               mempool_free(mpio, m->mpio_pool);
+
+       return r;
+}
+
+/*
+ * Suspend can't complete until all the I/O is processed so if
+ * the last path failed we will now error any queued I/O.
+ */
+static void multipath_presuspend(struct dm_target *ti)
+{
+       struct multipath *m = (struct multipath *) ti->private;
+       unsigned long flags;
+
+       spin_lock_irqsave(&m->lock, flags);
+       m->suspended = 1;
+       if (m->queue_if_no_path)
+               queue_work(kmultipathd, &m->process_queued_ios);
+       spin_unlock_irqrestore(&m->lock, flags);
+}
+
+static void multipath_resume(struct dm_target *ti)
+{
+       struct multipath *m = (struct multipath *) ti->private;
+       unsigned long flags;
+
+       spin_lock_irqsave(&m->lock, flags);
+       m->suspended = 0;
+       spin_unlock_irqrestore(&m->lock, flags);
+}
+
+/*
+ * Info output has the following format:
+ * num_multipath_feature_args [multipath_feature_args]*
+ * num_handler_status_args [handler_status_args]*
+ * num_groups init_group_number
+ *            [A|D|E num_ps_status_args [ps_status_args]*
+ *             num_paths num_selector_args
+ *             [path_dev A|F fail_count [selector_args]* ]+ ]+
+ *
+ * Table output has the following format (identical to the constructor string):
+ * num_feature_args [features_args]*
+ * num_handler_args hw_handler [hw_handler_args]*
+ * num_groups init_group_number
+ *     [priority selector-name num_ps_args [ps_args]*
+ *      num_paths num_selector_args [path_dev [selector_args]* ]+ ]+
+ */
+static int multipath_status(struct dm_target *ti, status_type_t type,
+                           char *result, unsigned int maxlen)
+{
+       int sz = 0;
+       unsigned long flags;
+       struct multipath *m = (struct multipath *) ti->private;
+       struct hw_handler *hwh = &m->hw_handler;
+       struct priority_group *pg;
+       struct pgpath *p;
+       unsigned pg_num;
+       char state;
+
+       spin_lock_irqsave(&m->lock, flags);
+
+       /* Features */
+       if (type == STATUSTYPE_INFO)
+               DMEMIT("1 %u ", m->queue_size);
+       else if (m->queue_if_no_path)
+               DMEMIT("1 queue_if_no_path ");
+       else
+               DMEMIT("0 ");
+
+       if (hwh->type && hwh->type->status)
+               sz += hwh->type->status(hwh, type, result + sz, maxlen - sz);
+       else if (!hwh->type || type == STATUSTYPE_INFO)
+               DMEMIT("0 ");
+       else
+               DMEMIT("1 %s ", hwh->type->name);
+
+       DMEMIT("%u ", m->nr_priority_groups);
+
+       if (m->next_pg)
+               pg_num = m->next_pg->pg_num;
+       else if (m->current_pg)
+               pg_num = m->current_pg->pg_num;
+       else
+                       pg_num = 1;
+
+       DMEMIT("%u ", pg_num);
+
+       switch (type) {
+       case STATUSTYPE_INFO:
+               list_for_each_entry(pg, &m->priority_groups, list) {
+                       if (pg->bypassed)
+                               state = 'D';    /* Disabled */
+                       else if (pg == m->current_pg)
+                               state = 'A';    /* Currently Active */
+                       else
+                               state = 'E';    /* Enabled */
+
+                       DMEMIT("%c ", state);
+
+                       if (pg->ps.type->status)
+                               sz += pg->ps.type->status(&pg->ps, NULL, type,
+                                                         result + sz,
+                                                         maxlen - sz);
+                       else
+                               DMEMIT("0 ");
+
+                       DMEMIT("%u %u ", pg->nr_pgpaths,
+                              pg->ps.type->info_args);
+
+                       list_for_each_entry(p, &pg->pgpaths, list) {
+                               DMEMIT("%s %s %u ", p->path.dev->name,
+                                      p->path.is_active ? "A" : "F",
+                                      p->fail_count);
+                               if (pg->ps.type->status)
+                                       sz += pg->ps.type->status(&pg->ps,
+                                             &p->path, type, result + sz,
+                                             maxlen - sz);
+                       }
+               }
+               break;
+
+       case STATUSTYPE_TABLE:
+               list_for_each_entry(pg, &m->priority_groups, list) {
+                       DMEMIT("%s ", pg->ps.type->name);
+
+                       if (pg->ps.type->status)
+                               sz += pg->ps.type->status(&pg->ps, NULL, type,
+                                                         result + sz,
+                                                         maxlen - sz);
+                       else
+                               DMEMIT("0 ");
+
+                       DMEMIT("%u %u ", pg->nr_pgpaths,
+                              pg->ps.type->table_args);
+
+                       list_for_each_entry(p, &pg->pgpaths, list) {
+                               DMEMIT("%s ", p->path.dev->name);
+                               if (pg->ps.type->status)
+                                       sz += pg->ps.type->status(&pg->ps,
+                                             &p->path, type, result + sz,
+                                             maxlen - sz);
+                       }
+               }
+               break;
+       }
+
+       spin_unlock_irqrestore(&m->lock, flags);
+
+       return 0;
+}
+
+static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
+{
+       int r;
+       struct dm_dev *dev;
+       struct multipath *m = (struct multipath *) ti->private;
+       action_fn action;
+
+       if (argc == 1) {
+               if (!strnicmp(argv[0], MESG_STR("queue_if_no_path")))
+                       return queue_if_no_path(m, 1);
+               else if (!strnicmp(argv[0], MESG_STR("fail_if_no_path")))
+                       return queue_if_no_path(m, 0);
+       }
+
+       if (argc != 2)
+               goto error;
+
+       if (!strnicmp(argv[0], MESG_STR("disable_group")))
+               return bypass_pg_num(m, argv[1], 1);
+       else if (!strnicmp(argv[0], MESG_STR("enable_group")))
+               return bypass_pg_num(m, argv[1], 0);
+       else if (!strnicmp(argv[0], MESG_STR("switch_group")))
+               return switch_pg_num(m, argv[1]);
+       else if (!strnicmp(argv[0], MESG_STR("reinstate_path")))
+               action = reinstate_path;
+       else if (!strnicmp(argv[0], MESG_STR("fail_path")))
+               action = fail_path;
+       else
+               goto error;
+
+       r = dm_get_device(ti, argv[1], ti->begin, ti->len,
+                         dm_table_get_mode(ti->table), &dev);
+       if (r) {
+               DMWARN("dm-multipath message: error getting device %s",
+                      argv[1]);
+               return -EINVAL;
+       }
+
+       r = action_dev(m, dev, action);
+
+       dm_put_device(ti, dev);
+
+       return r;
+
+error:
+       DMWARN("Unrecognised multipath message received.");
+       return -EINVAL;
+}
+
+/*-----------------------------------------------------------------
+ * Module setup
+ *---------------------------------------------------------------*/
+static struct target_type multipath_target = {
+       .name = "multipath",
+       .version = {1, 0, 4},
+       .module = THIS_MODULE,
+       .ctr = multipath_ctr,
+       .dtr = multipath_dtr,
+       .map = multipath_map,
+       .end_io = multipath_end_io,
+       .presuspend = multipath_presuspend,
+       .resume = multipath_resume,
+       .status = multipath_status,
+       .message = multipath_message,
+};
+
+static int __init dm_multipath_init(void)
+{
+       int r;
+
+       /* allocate a slab for the dm_ios */
+       _mpio_cache = kmem_cache_create("dm_mpath", sizeof(struct mpath_io),
+                                       0, 0, NULL, NULL);
+       if (!_mpio_cache)
+               return -ENOMEM;
+
+       r = dm_register_target(&multipath_target);
+       if (r < 0) {
+               DMERR("%s: register failed %d", multipath_target.name, r);
+               kmem_cache_destroy(_mpio_cache);
+               return -EINVAL;
+       }
+
+       kmultipathd = create_workqueue("kmpathd");
+       if (!kmultipathd) {
+               DMERR("%s: failed to create workqueue kmpathd",
+                               multipath_target.name);
+               dm_unregister_target(&multipath_target);
+               kmem_cache_destroy(_mpio_cache);
+               return -ENOMEM;
+       }
+
+       DMINFO("dm-multipath version %u.%u.%u loaded",
+              multipath_target.version[0], multipath_target.version[1],
+              multipath_target.version[2]);
+
+       return r;
+}
+
+static void __exit dm_multipath_exit(void)
+{
+       int r;
+
+       destroy_workqueue(kmultipathd);
+
+       r = dm_unregister_target(&multipath_target);
+       if (r < 0)
+               DMERR("%s: target unregister failed %d",
+                     multipath_target.name, r);
+       kmem_cache_destroy(_mpio_cache);
+}
+
+EXPORT_SYMBOL_GPL(dm_pg_init_complete);
+
+module_init(dm_multipath_init);
+module_exit(dm_multipath_exit);
+
+MODULE_DESCRIPTION(DM_NAME " multipath target");
+MODULE_AUTHOR("Sistina Software <dm-devel@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm-mpath.h b/drivers/md/dm-mpath.h
new file mode 100644 (file)
index 0000000..8a4bf2b
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * This file is released under the GPL.
+ *
+ * Multipath.
+ */
+
+#ifndef        DM_MPATH_H
+#define        DM_MPATH_H
+
+struct dm_dev;
+
+struct path {
+       struct dm_dev *dev;     /* Read-only */
+       unsigned is_active;     /* Read-only */
+
+       void *pscontext;        /* For path-selector use */
+       void *hwhcontext;       /* For hw-handler use */
+};
+
+/* Callback for hwh_pg_init_fn to use when complete */
+void dm_pg_init_complete(struct path *path, unsigned err_flags);
+
+#endif
diff --git a/drivers/md/dm-path-selector.c b/drivers/md/dm-path-selector.c
new file mode 100644 (file)
index 0000000..a28c1c2
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2003 Sistina Software.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * Module Author: Heinz Mauelshagen
+ *
+ * This file is released under the GPL.
+ *
+ * Path selector registration.
+ */
+
+#include "dm.h"
+#include "dm-path-selector.h"
+
+#include <linux/slab.h>
+
+struct ps_internal {
+       struct path_selector_type pst;
+
+       struct list_head list;
+       long use;
+};
+
+#define pst_to_psi(__pst) container_of((__pst), struct ps_internal, pst)
+
+static LIST_HEAD(_path_selectors);
+static DECLARE_RWSEM(_ps_lock);
+
+static struct ps_internal *__find_path_selector_type(const char *name)
+{
+       struct ps_internal *psi;
+
+       list_for_each_entry(psi, &_path_selectors, list) {
+               if (!strcmp(name, psi->pst.name))
+                       return psi;
+       }
+
+       return NULL;
+}
+
+static struct ps_internal *get_path_selector(const char *name)
+{
+       struct ps_internal *psi;
+
+       down_read(&_ps_lock);
+       psi = __find_path_selector_type(name);
+       if (psi) {
+               if ((psi->use == 0) && !try_module_get(psi->pst.module))
+                       psi = NULL;
+               else
+                       psi->use++;
+       }
+       up_read(&_ps_lock);
+
+       return psi;
+}
+
+struct path_selector_type *dm_get_path_selector(const char *name)
+{
+       struct ps_internal *psi;
+
+       if (!name)
+               return NULL;
+
+       psi = get_path_selector(name);
+       if (!psi) {
+               request_module("dm-%s", name);
+               psi = get_path_selector(name);
+       }
+
+       return psi ? &psi->pst : NULL;
+}
+
+void dm_put_path_selector(struct path_selector_type *pst)
+{
+       struct ps_internal *psi;
+
+       if (!pst)
+               return;
+
+       down_read(&_ps_lock);
+       psi = __find_path_selector_type(pst->name);
+       if (!psi)
+               goto out;
+
+       if (--psi->use == 0)
+               module_put(psi->pst.module);
+
+       if (psi->use < 0)
+               BUG();
+
+out:
+       up_read(&_ps_lock);
+}
+
+static struct ps_internal *_alloc_path_selector(struct path_selector_type *pst)
+{
+       struct ps_internal *psi = kmalloc(sizeof(*psi), GFP_KERNEL);
+
+       if (psi) {
+               memset(psi, 0, sizeof(*psi));
+               psi->pst = *pst;
+       }
+
+       return psi;
+}
+
+int dm_register_path_selector(struct path_selector_type *pst)
+{
+       int r = 0;
+       struct ps_internal *psi = _alloc_path_selector(pst);
+
+       if (!psi)
+               return -ENOMEM;
+
+       down_write(&_ps_lock);
+
+       if (__find_path_selector_type(pst->name)) {
+               kfree(psi);
+               r = -EEXIST;
+       } else
+               list_add(&psi->list, &_path_selectors);
+
+       up_write(&_ps_lock);
+
+       return r;
+}
+
+int dm_unregister_path_selector(struct path_selector_type *pst)
+{
+       struct ps_internal *psi;
+
+       down_write(&_ps_lock);
+
+       psi = __find_path_selector_type(pst->name);
+       if (!psi) {
+               up_write(&_ps_lock);
+               return -EINVAL;
+       }
+
+       if (psi->use) {
+               up_write(&_ps_lock);
+               return -ETXTBSY;
+       }
+
+       list_del(&psi->list);
+
+       up_write(&_ps_lock);
+
+       kfree(psi);
+
+       return 0;
+}
+
+EXPORT_SYMBOL_GPL(dm_register_path_selector);
+EXPORT_SYMBOL_GPL(dm_unregister_path_selector);
diff --git a/drivers/md/dm-path-selector.h b/drivers/md/dm-path-selector.h
new file mode 100644 (file)
index 0000000..732d06a
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2003 Sistina Software.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ *
+ * Module Author: Heinz Mauelshagen
+ *
+ * This file is released under the GPL.
+ *
+ * Path-Selector registration.
+ */
+
+#ifndef        DM_PATH_SELECTOR_H
+#define        DM_PATH_SELECTOR_H
+
+#include <linux/device-mapper.h>
+
+#include "dm-mpath.h"
+
+/*
+ * We provide an abstraction for the code that chooses which path
+ * to send some io down.
+ */
+struct path_selector_type;
+struct path_selector {
+       struct path_selector_type *type;
+       void *context;
+};
+
+/* Information about a path selector type */
+struct path_selector_type {
+       char *name;
+       struct module *module;
+
+       unsigned int table_args;
+       unsigned int info_args;
+
+       /*
+        * Constructs a path selector object, takes custom arguments
+        */
+       int (*create) (struct path_selector *ps, unsigned argc, char **argv);
+       void (*destroy) (struct path_selector *ps);
+
+       /*
+        * Add an opaque path object, along with some selector specific
+        * path args (eg, path priority).
+        */
+       int (*add_path) (struct path_selector *ps, struct path *path,
+                        int argc, char **argv, char **error);
+
+       /*
+        * Chooses a path for this io, if no paths are available then
+        * NULL will be returned.
+        *
+        * repeat_count is the number of times to use the path before
+        * calling the function again.  0 means don't call it again unless
+        * the path fails.
+        */
+       struct path *(*select_path) (struct path_selector *ps,
+                                    unsigned *repeat_count);
+
+       /*
+        * Notify the selector that a path has failed.
+        */
+       void (*fail_path) (struct path_selector *ps, struct path *p);
+
+       /*
+        * Ask selector to reinstate a path.
+        */
+       int (*reinstate_path) (struct path_selector *ps, struct path *p);
+
+       /*
+        * Table content based on parameters added in ps_add_path_fn
+        * or path selector status
+        */
+       int (*status) (struct path_selector *ps, struct path *path,
+                      status_type_t type, char *result, unsigned int maxlen);
+
+       int (*end_io) (struct path_selector *ps, struct path *path);
+};
+
+/* Register a path selector */
+int dm_register_path_selector(struct path_selector_type *type);
+
+/* Unregister a path selector */
+int dm_unregister_path_selector(struct path_selector_type *type);
+
+/* Returns a registered path selector type */
+struct path_selector_type *dm_get_path_selector(const char *name);
+
+/* Releases a path selector  */
+void dm_put_path_selector(struct path_selector_type *pst);
+
+#endif
diff --git a/drivers/md/dm-round-robin.c b/drivers/md/dm-round-robin.c
new file mode 100644 (file)
index 0000000..d002486
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2003 Sistina Software.
+ * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ *
+ * Module Author: Heinz Mauelshagen
+ *
+ * This file is released under the GPL.
+ *
+ * Round-robin path selector.
+ */
+
+#include "dm.h"
+#include "dm-path-selector.h"
+
+#include <linux/slab.h>
+
+/*-----------------------------------------------------------------
+ * Path-handling code, paths are held in lists
+ *---------------------------------------------------------------*/
+struct path_info {
+       struct list_head list;
+       struct path *path;
+       unsigned repeat_count;
+};
+
+static void free_paths(struct list_head *paths)
+{
+       struct path_info *pi, *next;
+
+       list_for_each_entry_safe(pi, next, paths, list) {
+               list_del(&pi->list);
+               kfree(pi);
+       }
+}
+
+/*-----------------------------------------------------------------
+ * Round-robin selector
+ *---------------------------------------------------------------*/
+
+#define RR_MIN_IO              1000
+
+struct selector {
+       struct list_head valid_paths;
+       struct list_head invalid_paths;
+};
+
+static struct selector *alloc_selector(void)
+{
+       struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL);
+
+       if (s) {
+               INIT_LIST_HEAD(&s->valid_paths);
+               INIT_LIST_HEAD(&s->invalid_paths);
+       }
+
+       return s;
+}
+
+static int rr_create(struct path_selector *ps, unsigned argc, char **argv)
+{
+       struct selector *s;
+
+       s = alloc_selector();
+       if (!s)
+               return -ENOMEM;
+
+       ps->context = s;
+       return 0;
+}
+
+static void rr_destroy(struct path_selector *ps)
+{
+       struct selector *s = (struct selector *) ps->context;
+
+       free_paths(&s->valid_paths);
+       free_paths(&s->invalid_paths);
+       kfree(s);
+       ps->context = NULL;
+}
+
+static int rr_status(struct path_selector *ps, struct path *path,
+                    status_type_t type, char *result, unsigned int maxlen)
+{
+       struct path_info *pi;
+       int sz = 0;
+
+       if (!path)
+               DMEMIT("0 ");
+       else {
+               switch(type) {
+               case STATUSTYPE_INFO:
+                       break;
+               case STATUSTYPE_TABLE:
+                       pi = path->pscontext;
+                       DMEMIT("%u ", pi->repeat_count);
+                       break;
+               }
+       }
+
+       return sz;
+}
+
+/*
+ * Called during initialisation to register each path with an
+ * optional repeat_count.
+ */
+static int rr_add_path(struct path_selector *ps, struct path *path,
+                      int argc, char **argv, char **error)
+{
+       struct selector *s = (struct selector *) ps->context;
+       struct path_info *pi;
+       unsigned repeat_count = RR_MIN_IO;
+
+       if (argc > 1) {
+               *error = "round-robin ps: incorrect number of arguments";
+               return -EINVAL;
+       }
+
+       /* First path argument is number of I/Os before switching path */
+       if ((argc == 1) && (sscanf(argv[0], "%u", &repeat_count) != 1)) {
+               *error = "round-robin ps: invalid repeat count";
+               return -EINVAL;
+       }
+
+       /* allocate the path */
+       pi = kmalloc(sizeof(*pi), GFP_KERNEL);
+       if (!pi) {
+               *error = "round-robin ps: Error allocating path context";
+               return -ENOMEM;
+       }
+
+       pi->path = path;
+       pi->repeat_count = repeat_count;
+
+       path->pscontext = pi;
+
+       list_add(&pi->list, &s->valid_paths);
+
+       return 0;
+}
+
+static void rr_fail_path(struct path_selector *ps, struct path *p)
+{
+       struct selector *s = (struct selector *) ps->context;
+       struct path_info *pi = p->pscontext;
+
+       list_move(&pi->list, &s->invalid_paths);
+}
+
+static int rr_reinstate_path(struct path_selector *ps, struct path *p)
+{
+       struct selector *s = (struct selector *) ps->context;
+       struct path_info *pi = p->pscontext;
+
+       list_move(&pi->list, &s->valid_paths);
+
+       return 0;
+}
+
+static struct path *rr_select_path(struct path_selector *ps,
+                                  unsigned *repeat_count)
+{
+       struct selector *s = (struct selector *) ps->context;
+       struct path_info *pi = NULL;
+
+       if (!list_empty(&s->valid_paths)) {
+               pi = list_entry(s->valid_paths.next, struct path_info, list);
+               list_move_tail(&pi->list, &s->valid_paths);
+               *repeat_count = pi->repeat_count;
+       }
+
+       return pi ? pi->path : NULL;
+}
+
+static struct path_selector_type rr_ps = {
+       .name = "round-robin",
+       .module = THIS_MODULE,
+       .table_args = 1,
+       .info_args = 0,
+       .create = rr_create,
+       .destroy = rr_destroy,
+       .status = rr_status,
+       .add_path = rr_add_path,
+       .fail_path = rr_fail_path,
+       .reinstate_path = rr_reinstate_path,
+       .select_path = rr_select_path,
+};
+
+static int __init dm_rr_init(void)
+{
+       int r = dm_register_path_selector(&rr_ps);
+
+       if (r < 0)
+               DMERR("round-robin: register failed %d", r);
+
+       DMINFO("dm-round-robin version 1.0.0 loaded");
+
+       return r;
+}
+
+static void __exit dm_rr_exit(void)
+{
+       int r = dm_unregister_path_selector(&rr_ps);
+
+       if (r < 0)
+               DMERR("round-robin: unregister failed %d", r);
+}
+
+module_init(dm_rr_init);
+module_exit(dm_rr_exit);
+
+MODULE_DESCRIPTION(DM_NAME " round-robin multipath path selector");
+MODULE_AUTHOR("Sistina Software <dm-devel@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/b2c2/flexcop-common.h b/drivers/media/dvb/b2c2/flexcop-common.h
new file mode 100644 (file)
index 0000000..773d158
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+ *
+ * flexcop-common.h - common header file for device-specific source files also.
+ *
+ * see flexcop.c for copyright information.
+ */
+#ifndef __FLEXCOP_COMMON_H__
+#define __FLEXCOP_COMMON_H__
+
+#include <linux/config.h>
+#include <linux/pci.h>
+
+#include "flexcop-reg.h"
+
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_filter.h"
+#include "dvb_net.h"
+#include "dvb_frontend.h"
+
+#define FC_MAX_FEED 256
+
+#ifndef FC_LOG_PREFIX
+#warning please define a log prefix for your file, using a default one
+#define FC_LOG_PREFIX "b2c2-undef"
+#endif
+
+/* Steal from usb.h */
+#undef err
+#define err(format,  arg...) printk(KERN_ERR     FC_LOG_PREFIX ": " format "\n" , ## arg)
+#undef info
+#define info(format, arg...) printk(KERN_INFO    FC_LOG_PREFIX ": " format "\n" , ## arg)
+#undef warn
+#define warn(format, arg...) printk(KERN_WARNING FC_LOG_PREFIX ": " format "\n" , ## arg)
+
+struct flexcop_dma {
+       struct pci_dev *pdev;
+
+       u8 *cpu_addr0;
+       dma_addr_t dma_addr0;
+       u8 *cpu_addr1;
+       dma_addr_t dma_addr1;
+       u32 size; /* size of each address in bytes */
+};
+
+/* Control structure for data definitions that are common to
+ * the B2C2-based PCI and USB devices.
+ */
+struct flexcop_device {
+       /* general */
+       struct device *dev; /* for firmware_class */
+
+#define FC_STATE_DVB_INIT 0x01
+#define FC_STATE_I2C_INIT 0x02
+#define FC_STATE_FE_INIT  0x04
+       int init_state;
+
+       /* device information */
+       int has_32_hw_pid_filter;
+       flexcop_revision_t rev;
+       flexcop_device_type_t dev_type;
+       flexcop_bus_t bus_type;
+
+       /* dvb stuff */
+       struct dvb_adapter dvb_adapter;
+       struct dvb_frontend *fe;
+       struct dvb_net dvbnet;
+       struct dvb_demux demux;
+       struct dmxdev dmxdev;
+       struct dmx_frontend hw_frontend;
+       struct dmx_frontend mem_frontend;
+       int (*fe_sleep) (struct dvb_frontend *);
+
+       struct i2c_adapter i2c_adap;
+       struct semaphore i2c_sem;
+
+       struct module *owner;
+
+       /* options and status */
+       int extra_feedcount;
+       int feedcount;
+       int pid_filtering;
+       int fullts_streaming_state;
+
+       /* bus specific callbacks */
+       flexcop_ibi_value (*read_ibi_reg)  (struct flexcop_device *, flexcop_ibi_register);
+       int               (*write_ibi_reg) (struct flexcop_device *, flexcop_ibi_register, flexcop_ibi_value);
+
+
+       int (*i2c_request) (struct flexcop_device*, flexcop_access_op_t, flexcop_i2c_port_t, u8 chipaddr, u8 addr, u8 *buf, u16 len);
+       int (*stream_control) (struct flexcop_device*, int);
+
+       int (*get_mac_addr) (struct flexcop_device *fc, int extended);
+
+       void *bus_specific;
+};
+
+/* exported prototypes */
+
+/* from flexcop.c */
+void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len);
+void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no);
+
+struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len);
+void flexcop_device_kfree(struct flexcop_device*);
+
+int  flexcop_device_initialize(struct flexcop_device*);
+void flexcop_device_exit(struct flexcop_device *fc);
+
+/* from flexcop-dma.c */
+int flexcop_dma_allocate(struct pci_dev *pdev, struct flexcop_dma *dma, u32 size);
+void flexcop_dma_free(struct flexcop_dma *dma);
+
+int flexcop_dma_control_timer_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff);
+int flexcop_dma_control_size_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff);
+int flexcop_dma_control_packet_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff);
+int flexcop_dma_config(struct flexcop_device *fc, struct flexcop_dma *dma, flexcop_dma_index_t dma_idx,flexcop_dma_addr_index_t index);
+int flexcop_dma_config_timer(struct flexcop_device *fc, flexcop_dma_index_t dma_idx, u8 cycles);
+int flexcop_dma_config_packet_count(struct flexcop_device *fc, flexcop_dma_index_t dma_idx, u8 packets);
+
+/* from flexcop-eeprom.c */
+/* the PCI part uses this call to get the MAC address, the USB part has its own */
+int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended);
+
+/* from flexcop-i2c.c */
+/* the PCI part uses this a i2c_request callback, whereas the usb part has its own
+ * one. We have it in flexcop-i2c.c, because it is going via the actual
+ * I2C-channel of the flexcop.
+ */
+int flexcop_i2c_request(struct flexcop_device*, flexcop_access_op_t,
+                       flexcop_i2c_port_t, u8 chipaddr, u8 addr, u8 *buf, u16 len);
+
+/* from flexcop-sram.c */
+int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, flexcop_sram_dest_target_t target);
+void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s);
+void flexcop_sram_ctrl(struct flexcop_device *fc, int usb_wan, int sramdma, int maximumfill);
+
+/* global prototypes for the flexcop-chip */
+/* from flexcop-fe-tuner.c */
+int flexcop_frontend_init(struct flexcop_device *card);
+void flexcop_frontend_exit(struct flexcop_device *fc);
+
+/* from flexcop-i2c.c */
+int flexcop_i2c_init(struct flexcop_device *fc);
+void flexcop_i2c_exit(struct flexcop_device *fc);
+
+/* from flexcop-sram.c */
+int flexcop_sram_init(struct flexcop_device *fc);
+
+/* from flexcop-misc.c */
+void flexcop_determine_revision(struct flexcop_device *fc);
+void flexcop_device_name(struct flexcop_device *fc,const char *prefix,const char *suffix);
+
+/* from flexcop-hw-filter.c */
+int flexcop_pid_feed_control(struct flexcop_device *fc, struct dvb_demux_feed *dvbdmxfeed, int onoff);
+void flexcop_hw_filter_init(struct flexcop_device *fc);
+
+void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff);
+
+void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6]);
+void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff);
+
+#endif
diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
new file mode 100644 (file)
index 0000000..71be400
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+ *
+ * flexcop-fe-tuner.c - methods for attaching a frontend and controlling DiSEqC.
+ *
+ * see flexcop.c for copyright information.
+ */
+#include "flexcop.h"
+
+#include "stv0299.h"
+#include "mt352.h"
+#include "nxt2002.h"
+#include "stv0297.h"
+#include "mt312.h"
+
+/* lnb control */
+
+static int flexcop_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+       struct flexcop_device *fc = fe->dvb->priv;
+       flexcop_ibi_value v;
+       deb_tuner("polarity/voltage = %u\n", voltage);
+
+       v = fc->read_ibi_reg(fc, misc_204);
+       switch (voltage) {
+               case SEC_VOLTAGE_OFF:
+                       v.misc_204.ACPI1_sig = 1;
+                       break;
+               case SEC_VOLTAGE_13:
+                       v.misc_204.ACPI1_sig = 0;
+                       v.misc_204.LNB_L_H_sig = 0;
+                       break;
+               case SEC_VOLTAGE_18:
+                       v.misc_204.ACPI1_sig = 0;
+                       v.misc_204.LNB_L_H_sig = 1;
+                       break;
+               default:
+                       err("unknown SEC_VOLTAGE value");
+                       return -EINVAL;
+       }
+       return fc->write_ibi_reg(fc, misc_204, v);
+}
+
+static int flexcop_sleep(struct dvb_frontend* fe)
+{
+       struct flexcop_device *fc = fe->dvb->priv;
+/*     flexcop_ibi_value v = fc->read_ibi_reg(fc,misc_204); */
+
+       if (fc->fe_sleep)
+               return fc->fe_sleep(fe);
+
+/*     v.misc_204.ACPI3_sig = 1;
+       fc->write_ibi_reg(fc,misc_204,v);*/
+
+       return 0;
+}
+
+static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+       /* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */
+       struct flexcop_device *fc = fe->dvb->priv;
+       flexcop_ibi_value v;
+       u16 ax;
+       v.raw = 0;
+
+       deb_tuner("tone = %u\n",tone);
+
+       switch (tone) {
+               case SEC_TONE_ON:
+                       ax = 0x01ff;
+                       break;
+               case SEC_TONE_OFF:
+                       ax = 0;
+                       break;
+               default:
+                       err("unknown SEC_TONE value");
+                       return -EINVAL;
+       }
+
+       v.lnb_switch_freq_200.LNB_CTLPrescaler_sig = 1; /* divide by 2 */
+
+       v.lnb_switch_freq_200.LNB_CTLHighCount_sig = ax;
+       v.lnb_switch_freq_200.LNB_CTLLowCount_sig  = ax == 0 ? 0x1ff : ax;
+
+       return fc->write_ibi_reg(fc,lnb_switch_freq_200,v);
+}
+
+static void flexcop_diseqc_send_bit(struct dvb_frontend* fe, int data)
+{
+       flexcop_set_tone(fe, SEC_TONE_ON);
+       udelay(data ? 500 : 1000);
+       flexcop_set_tone(fe, SEC_TONE_OFF);
+       udelay(data ? 1000 : 500);
+}
+
+static void flexcop_diseqc_send_byte(struct dvb_frontend* fe, int data)
+{
+       int i, par = 1, d;
+
+       for (i = 7; i >= 0; i--) {
+               d = (data >> i) & 1;
+               par ^= d;
+               flexcop_diseqc_send_bit(fe, d);
+       }
+
+       flexcop_diseqc_send_bit(fe, par);
+}
+
+static int flexcop_send_diseqc_msg(struct dvb_frontend* fe, int len, u8 *msg, unsigned long burst)
+{
+       int i;
+
+       flexcop_set_tone(fe, SEC_TONE_OFF);
+       mdelay(16);
+
+       for (i = 0; i < len; i++)
+               flexcop_diseqc_send_byte(fe,msg[i]);
+
+       mdelay(16);
+
+       if (burst != -1) {
+               if (burst)
+                       flexcop_diseqc_send_byte(fe, 0xff);
+               else {
+                       flexcop_set_tone(fe, SEC_TONE_ON);
+                       udelay(12500);
+                       flexcop_set_tone(fe, SEC_TONE_OFF);
+               }
+               msleep(20);
+       }
+       return 0;
+}
+
+static int flexcop_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+{
+       return flexcop_send_diseqc_msg(fe, cmd->msg_len, cmd->msg, 0);
+}
+
+static int flexcop_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+       return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd);
+}
+
+/* dvb-s stv0299 */
+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 flexcop_device *fc = 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(&fc->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,
+};
+
+/* dvb-t mt352 */
+static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
+{
+       static u8 mt352_clock_config [] = { 0x89, 0x18, 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;
+}
+
+static 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 nxt2002_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
+{
+       struct flexcop_device *fc = fe->dvb->priv;
+       return request_firmware(fw, name, fc->dev);
+}
+
+static struct nxt2002_config samsung_tbmv_config = {
+       .demod_address = 0x0a,
+       .request_firmware = nxt2002_request_firmware,
+};
+
+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 flexcop_device *fc = 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(&fc->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,
+};
+
+static struct stv0297_config alps_tdee4_stv0297_config = {
+       .demod_address = 0x1c,
+//     .invert = 1,
+//     .pll_set = alps_tdee4_stv0297_pll_set,
+};
+
+/* try to figure out the frontend, each card/box can have on of the following list */
+int flexcop_frontend_init(struct flexcop_device *fc)
+{
+       /* try the sky v2.6 (stv0299/Samsung tbmu24112(sl1935)) */
+       if ((fc->fe = stv0299_attach(&samsung_tbmu24112_config, &fc->i2c_adap)) != NULL) {
+               fc->fe->ops->set_voltage = flexcop_set_voltage;
+
+               fc->fe_sleep             = fc->fe->ops->sleep;
+               fc->fe->ops->sleep       = flexcop_sleep;
+
+               fc->dev_type          = FC_SKY;
+               info("found the stv0299 at i2c address: 0x%02x",samsung_tbmu24112_config.demod_address);
+       } else
+       /* try the air dvb-t (mt352/Samsung tdtc9251dh0(??)) */
+       if ((fc->fe = mt352_attach(&samsung_tdtc9251dh0_config, &fc->i2c_adap)) != NULL ) {
+               fc->dev_type          = FC_AIR_DVB;
+               info("found the mt352 at i2c address: 0x%02x",samsung_tdtc9251dh0_config.demod_address);
+       } else
+       /* try the air atsc (nxt2002) */
+       if ((fc->fe = nxt2002_attach(&samsung_tbmv_config, &fc->i2c_adap)) != NULL) {
+               fc->dev_type          = FC_AIR_ATSC;
+               info("found the nxt2002 at i2c address: 0x%02x",samsung_tbmv_config.demod_address);
+       } else
+       /* try the cable dvb (stv0297) */
+       if ((fc->fe = stv0297_attach(&alps_tdee4_stv0297_config, &fc->i2c_adap, 0xf8)) != NULL) {
+               fc->dev_type                        = FC_CABLE;
+               info("found the stv0297 at i2c address: 0x%02x",alps_tdee4_stv0297_config.demod_address);
+       } else
+       /* try the sky v2.3 (vp310/Samsung tbdu18132(tsa5059)) */
+       if ((fc->fe = vp310_attach(&skystar23_samsung_tbdu18132_config, &fc->i2c_adap)) != NULL) {
+               fc->fe->ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
+               fc->fe->ops->diseqc_send_burst      = flexcop_diseqc_send_burst;
+               fc->fe->ops->set_tone               = flexcop_set_tone;
+               fc->fe->ops->set_voltage            = flexcop_set_voltage;
+
+               fc->fe_sleep                        = fc->fe->ops->sleep;
+               fc->fe->ops->sleep                  = flexcop_sleep;
+
+               fc->dev_type                        = FC_SKY_OLD;
+               info("found the vp310 (aka mt312) at i2c address: 0x%02x",skystar23_samsung_tbdu18132_config.demod_address);
+       }
+
+       if (fc->fe == NULL) {
+               err("no frontend driver found for this B2C2/FlexCop adapter");
+               return -ENODEV;
+       } else {
+               if (dvb_register_frontend(&fc->dvb_adapter, fc->fe)) {
+                       err("frontend registration failed!");
+                       if (fc->fe->ops->release != NULL)
+                               fc->fe->ops->release(fc->fe);
+                       fc->fe = NULL;
+                       return -EINVAL;
+               }
+       }
+       fc->init_state |= FC_STATE_FE_INIT;
+       return 0;
+}
+
+void flexcop_frontend_exit(struct flexcop_device *fc)
+{
+       if (fc->init_state & FC_STATE_FE_INIT)
+               dvb_unregister_frontend(fc->fe);
+
+       fc->init_state &= ~FC_STATE_FE_INIT;
+}
diff --git a/drivers/media/dvb/b2c2/flexcop-i2c.c b/drivers/media/dvb/b2c2/flexcop-i2c.c
new file mode 100644 (file)
index 0000000..be4266d
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+ *
+ * flexcop-i2c.c - flexcop internal 2Wire bus (I2C) and dvb i2c initialization
+ *
+ * see flexcop.c for copyright information.
+ */
+#include "flexcop.h"
+
+#define FC_MAX_I2C_RETRIES 100000
+
+static int flexcop_i2c_operation(struct flexcop_device *fc, flexcop_ibi_value *r100)
+{
+       int i;
+       flexcop_ibi_value r;
+
+       r100->tw_sm_c_100.working_start = 1;
+       deb_i2c("r100 before: %08x\n",r100->raw);
+
+       fc->write_ibi_reg(fc, tw_sm_c_100, ibi_zero);
+       fc->write_ibi_reg(fc, tw_sm_c_100, *r100); /* initiating i2c operation */
+
+       for (i = 0; i < FC_MAX_I2C_RETRIES; i++) {
+               r = fc->read_ibi_reg(fc, tw_sm_c_100);
+
+               if (!r.tw_sm_c_100.no_base_addr_ack_error) {
+                       if (r.tw_sm_c_100.st_done) {  /* && !r.tw_sm_c_100.working_start */
+                               *r100 = r;
+                               deb_i2c("i2c success\n");
+                               return 0;
+                       }
+               } else {
+                       deb_i2c("suffering from an i2c ack_error\n");
+                       return -EREMOTEIO;
+               }
+       }
+       deb_i2c("tried %d times i2c operation, never finished or too many ack errors.\n",i);
+       return -EREMOTEIO;
+}
+
+static int flexcop_i2c_read4(struct flexcop_device *fc, flexcop_ibi_value r100, u8 *buf)
+{
+       flexcop_ibi_value r104;
+       int len = r100.tw_sm_c_100.total_bytes, /* remember total_bytes is buflen-1 */
+               ret;
+
+       if ((ret = flexcop_i2c_operation(fc,&r100)) != 0) {
+               /* The Cablestar needs a different kind of i2c-transfer (does not
+                * support "Repeat Start"):
+                * wait for the ACK failure,
+                * and do a subsequent read with the Bit 30 enabled
+                */
+               r100.tw_sm_c_100.no_base_addr_ack_error = 1;
+               if ((ret = flexcop_i2c_operation(fc,&r100)) != 0) {
+                       deb_i2c("no_base_addr read failed. %d\n",ret);
+                       return ret;
+               }
+       }
+
+       buf[0] = r100.tw_sm_c_100.data1_reg;
+
+       if (len > 0) {
+               r104 = fc->read_ibi_reg(fc,tw_sm_c_104);
+               deb_i2c("read: r100: %08x, r104: %08x\n",r100.raw,r104.raw);
+
+               /* there is at least one more byte, otherwise we wouldn't be here */
+               buf[1] = r104.tw_sm_c_104.data2_reg;
+               if (len > 1) buf[2] = r104.tw_sm_c_104.data3_reg;
+               if (len > 2) buf[3] = r104.tw_sm_c_104.data4_reg;
+       }
+
+       return 0;
+}
+
+static int flexcop_i2c_write4(struct flexcop_device *fc, flexcop_ibi_value r100, u8 *buf)
+{
+       flexcop_ibi_value r104;
+       int len = r100.tw_sm_c_100.total_bytes; /* remember total_bytes is buflen-1 */
+       r104.raw = 0;
+
+       /* there is at least one byte, otherwise we wouldn't be here */
+       r100.tw_sm_c_100.data1_reg = buf[0];
+
+       r104.tw_sm_c_104.data2_reg = len > 0 ? buf[1] : 0;
+       r104.tw_sm_c_104.data3_reg = len > 1 ? buf[2] : 0;
+       r104.tw_sm_c_104.data4_reg = len > 2 ? buf[3] : 0;
+
+       deb_i2c("write: r100: %08x, r104: %08x\n",r100.raw,r104.raw);
+
+       /* write the additional i2c data before doing the actual i2c operation */
+       fc->write_ibi_reg(fc,tw_sm_c_104,r104);
+       return flexcop_i2c_operation(fc,&r100);
+}
+
+int flexcop_i2c_request(struct flexcop_device *fc, flexcop_access_op_t op,
+               flexcop_i2c_port_t port, u8 chipaddr, u8 addr, u8 *buf, u16 len)
+{
+       int ret;
+       u16 bytes_to_transfer;
+       flexcop_ibi_value r100;
+
+       deb_i2c("op = %d\n",op);
+       r100.raw = 0;
+       r100.tw_sm_c_100.chipaddr = chipaddr;
+       r100.tw_sm_c_100.twoWS_rw = op;
+       r100.tw_sm_c_100.twoWS_port_reg = port;
+
+       while (len != 0) {
+               bytes_to_transfer = len > 4 ? 4 : len;
+
+               r100.tw_sm_c_100.total_bytes = bytes_to_transfer - 1;
+               r100.tw_sm_c_100.baseaddr = addr;
+
+               if (op == FC_READ)
+                       ret = flexcop_i2c_read4(fc, r100, buf);
+               else
+                       ret = flexcop_i2c_write4(fc,r100, buf);
+
+               if (ret < 0)
+                       return ret;
+
+               buf  += bytes_to_transfer;
+               addr += bytes_to_transfer;
+               len  -= bytes_to_transfer;
+       };
+
+       return 0;
+}
+/* exported for PCI i2c */
+EXPORT_SYMBOL(flexcop_i2c_request);
+
+/* master xfer callback for demodulator */
+static int flexcop_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+{
+       struct flexcop_device *fc = i2c_get_adapdata(i2c_adap);
+       int i, ret = 0;
+
+       if (down_interruptible(&fc->i2c_sem))
+               return -ERESTARTSYS;
+
+       /* reading */
+       if (num == 2 &&
+               msgs[0].flags == 0 &&
+               msgs[1].flags == I2C_M_RD &&
+               msgs[0].buf != NULL &&
+               msgs[1].buf != NULL) {
+
+               ret = fc->i2c_request(fc, FC_READ, FC_I2C_PORT_DEMOD, msgs[0].addr, msgs[0].buf[0], msgs[1].buf, msgs[1].len);
+
+       } else for (i = 0; i < num; i++) { /* writing command */
+               if (msgs[i].flags != 0 || msgs[i].buf == NULL || msgs[i].len < 2) {
+                       ret = -EINVAL;
+                       break;
+               }
+
+               ret = fc->i2c_request(fc, FC_WRITE, FC_I2C_PORT_DEMOD, msgs[i].addr, msgs[i].buf[0], &msgs[i].buf[1], msgs[i].len - 1);
+       }
+
+       if (ret < 0)
+               err("i2c master_xfer failed");
+       else
+               ret = num;
+
+       up(&fc->i2c_sem);
+
+       return ret;
+}
+
+static u32 flexcop_i2c_func(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm flexcop_algo = {
+       .name                   = "FlexCop I2C algorithm",
+       .id                             = I2C_ALGO_BIT,
+       .master_xfer    = flexcop_master_xfer,
+       .functionality  = flexcop_i2c_func,
+};
+
+int flexcop_i2c_init(struct flexcop_device *fc)
+{
+       int ret;
+
+       sema_init(&fc->i2c_sem,1);
+
+       memset(&fc->i2c_adap, 0, sizeof(struct i2c_adapter));
+       strncpy(fc->i2c_adap.name, "B2C2 FlexCop device",I2C_NAME_SIZE);
+
+       i2c_set_adapdata(&fc->i2c_adap,fc);
+
+       fc->i2c_adap.class          = I2C_CLASS_TV_DIGITAL;
+       fc->i2c_adap.algo       = &flexcop_algo;
+       fc->i2c_adap.algo_data  = NULL;
+       fc->i2c_adap.id         = I2C_ALGO_BIT;
+
+       if ((ret = i2c_add_adapter(&fc->i2c_adap)) < 0)
+               return ret;
+
+       fc->init_state |= FC_STATE_I2C_INIT;
+       return 0;
+}
+
+void flexcop_i2c_exit(struct flexcop_device *fc)
+{
+       if (fc->init_state & FC_STATE_I2C_INIT)
+               i2c_del_adapter(&fc->i2c_adap);
+
+       fc->init_state &= ~FC_STATE_I2C_INIT;
+}
diff --git a/drivers/media/dvb/b2c2/flexcop-pci.c b/drivers/media/dvb/b2c2/flexcop-pci.c
new file mode 100644 (file)
index 0000000..ed717c0
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+ *
+ * flexcop-pci.c - covers the PCI part including DMA transfers.
+ *
+ * see flexcop.c for copyright information.
+ */
+
+#define FC_LOG_PREFIX "flexcop-pci"
+#include "flexcop-common.h"
+
+static int enable_pid_filtering = 1;
+module_param(enable_pid_filtering, int, 0444);
+MODULE_PARM_DESC(enable_pid_filtering, "enable hardware pid filtering: supported values: 0 (fullts), 1");
+
+#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
+#define dprintk(level,args...) \
+       do { if ((debug & level)) printk(args); } while (0)
+#define DEBSTATUS ""
+#else
+#define dprintk(level,args...)
+#define DEBSTATUS " (debugging is not enabled)"
+#endif
+
+#define deb_info(args...)  dprintk(0x01,args)
+#define deb_reg(args...)   dprintk(0x02,args)
+#define deb_ts(args...)    dprintk(0x04,args)
+#define deb_irq(args...)   dprintk(0x08,args)
+
+static int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debug level (1=info,2=regs,4=TS,8=irqdma (|-able))." DEBSTATUS);
+
+#define DRIVER_VERSION "0.1"
+#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV PCI Driver"
+#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de>"
+
+struct flexcop_pci {
+       struct pci_dev *pdev;
+
+#define FC_PCI_INIT     0x01
+#define FC_PCI_DMA_INIT 0x02
+       int init_state;
+
+       void __iomem *io_mem;
+       u32 irq;
+/* buffersize (at least for DMA1, need to be % 188 == 0,
+ * this logic is required */
+#define FC_DEFAULT_DMA1_BUFSIZE (1280 * 188)
+#define FC_DEFAULT_DMA2_BUFSIZE (10 * 188)
+       struct flexcop_dma dma[2];
+
+       int active_dma1_addr; /* 0 = addr0 of dma1; 1 = addr1 of dma1 */
+       u32 last_dma1_cur_pos; /* position of the pointer last time the timer/packet irq occured */
+       int count;
+
+       spinlock_t irq_lock;
+
+       struct flexcop_device *fc_dev;
+};
+
+static int lastwreg,lastwval,lastrreg,lastrval;
+
+static flexcop_ibi_value flexcop_pci_read_ibi_reg (struct flexcop_device *fc, flexcop_ibi_register r)
+{
+       struct flexcop_pci *fc_pci = fc->bus_specific;
+       flexcop_ibi_value v;
+       v.raw = readl(fc_pci->io_mem + r);
+
+       if (lastrreg != r || lastrval != v.raw) {
+               lastrreg = r; lastrval = v.raw;
+               deb_reg("new rd: %3x: %08x\n",r,v.raw);
+       }
+
+       return v;
+}
+
+static int flexcop_pci_write_ibi_reg(struct flexcop_device *fc, flexcop_ibi_register r, flexcop_ibi_value v)
+{
+       struct flexcop_pci *fc_pci = fc->bus_specific;
+
+       if (lastwreg != r || lastwval != v.raw) {
+               lastwreg = r; lastwval = v.raw;
+               deb_reg("new wr: %3x: %08x\n",r,v.raw);
+       }
+
+       writel(v.raw, fc_pci->io_mem + r);
+       return 0;
+}
+
+/* When PID filtering is turned on, we use the timer IRQ, because small amounts
+ * of data need to be passed to the user space instantly as well. When PID
+ * filtering is turned off, we use the page-change-IRQ */
+static irqreturn_t flexcop_pci_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct flexcop_pci *fc_pci = dev_id;
+       struct flexcop_device *fc = fc_pci->fc_dev;
+       flexcop_ibi_value v = fc->read_ibi_reg(fc,irq_20c);
+       irqreturn_t ret = IRQ_HANDLED;
+
+       spin_lock_irq(&fc_pci->irq_lock);
+
+       if (v.irq_20c.DMA1_IRQ_Status == 1) {
+               if (fc_pci->active_dma1_addr == 0)
+                       flexcop_pass_dmx_packets(fc_pci->fc_dev,fc_pci->dma[0].cpu_addr0,fc_pci->dma[0].size / 188);
+               else
+                       flexcop_pass_dmx_packets(fc_pci->fc_dev,fc_pci->dma[0].cpu_addr1,fc_pci->dma[0].size / 188);
+
+               deb_irq("page change to page: %d\n",!fc_pci->active_dma1_addr);
+               fc_pci->active_dma1_addr = !fc_pci->active_dma1_addr;
+       } else if (v.irq_20c.DMA1_Timer_Status == 1) {
+               /* for the timer IRQ we only can use buffer dmx feeding, because we don't have
+                * complete TS packets when reading from the DMA memory */
+               dma_addr_t cur_addr =
+                       fc->read_ibi_reg(fc,dma1_008).dma_0x8.dma_cur_addr << 2;
+               u32 cur_pos = cur_addr - fc_pci->dma[0].dma_addr0;
+
+               deb_irq("irq: %08x cur_addr: %08x: cur_pos: %08x, last_cur_pos: %08x ",
+                               v.raw,cur_addr,cur_pos,fc_pci->last_dma1_cur_pos);
+
+               /* buffer end was reached, restarted from the beginning
+                * pass the data from last_cur_pos to the buffer end to the demux
+                */
+               if (cur_pos < fc_pci->last_dma1_cur_pos) {
+                       deb_irq(" end was reached: passing %d bytes ",(fc_pci->dma[0].size*2 - 1) - fc_pci->last_dma1_cur_pos);
+                       flexcop_pass_dmx_data(fc_pci->fc_dev,
+                                       fc_pci->dma[0].cpu_addr0 + fc_pci->last_dma1_cur_pos,
+                                       (fc_pci->dma[0].size*2) - fc_pci->last_dma1_cur_pos);
+                       fc_pci->last_dma1_cur_pos = 0;
+                       fc_pci->count = 0;
+               }
+
+               if (cur_pos > fc_pci->last_dma1_cur_pos) {
+                       deb_irq(" passing %d bytes ",cur_pos - fc_pci->last_dma1_cur_pos);
+                       flexcop_pass_dmx_data(fc_pci->fc_dev,
+                                       fc_pci->dma[0].cpu_addr0 + fc_pci->last_dma1_cur_pos,
+                                       cur_pos - fc_pci->last_dma1_cur_pos);
+               }
+               deb_irq("\n");
+
+               fc_pci->last_dma1_cur_pos = cur_pos;
+       } else
+               ret = IRQ_NONE;
+
+       spin_unlock_irq(&fc_pci->irq_lock);
+
+/* packet count would be ideal for hw filtering, but it isn't working. Either
+ * the data book is wrong, or I'm unable to read it correctly */
+
+/*     if (v.irq_20c.DMA1_Size_IRQ_Status == 1) { packet counter */
+
+       return ret;
+}
+
+static int flexcop_pci_stream_control(struct flexcop_device *fc, int onoff)
+{
+       struct flexcop_pci *fc_pci = fc->bus_specific;
+       if (onoff) {
+               flexcop_dma_config(fc,&fc_pci->dma[0],FC_DMA_1,FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1);
+               flexcop_dma_config(fc,&fc_pci->dma[1],FC_DMA_2,FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1);
+               flexcop_dma_config_timer(fc,FC_DMA_1,1);
+
+               if (fc_pci->fc_dev->pid_filtering) {
+                       fc_pci->last_dma1_cur_pos = 0;
+                       flexcop_dma_control_timer_irq(fc,FC_DMA_1,1);
+               } else {
+                       fc_pci->active_dma1_addr = 0;
+                       flexcop_dma_control_size_irq(fc,FC_DMA_1,1);
+               }
+
+/*             flexcop_dma_config_packet_count(fc,FC_DMA_1,0xc0);
+               flexcop_dma_control_packet_irq(fc,FC_DMA_1,1); */
+
+               deb_irq("irqs enabled\n");
+       } else {
+               if (fc_pci->fc_dev->pid_filtering)
+                       flexcop_dma_control_timer_irq(fc,FC_DMA_1,0);
+               else
+                       flexcop_dma_control_size_irq(fc,FC_DMA_1,0);
+
+//             flexcop_dma_control_packet_irq(fc,FC_DMA_1,0);
+               deb_irq("irqs disabled\n");
+       }
+
+       return 0;
+}
+
+static int flexcop_pci_dma_init(struct flexcop_pci *fc_pci)
+{
+       int ret;
+       if ((ret = flexcop_dma_allocate(fc_pci->pdev,&fc_pci->dma[0],FC_DEFAULT_DMA1_BUFSIZE)) != 0)
+               return ret;
+
+       if ((ret = flexcop_dma_allocate(fc_pci->pdev,&fc_pci->dma[1],FC_DEFAULT_DMA2_BUFSIZE)) != 0)
+               goto dma1_free;
+
+       flexcop_sram_set_dest(fc_pci->fc_dev,FC_SRAM_DEST_MEDIA | FC_SRAM_DEST_NET, FC_SRAM_DEST_TARGET_DMA1);
+       flexcop_sram_set_dest(fc_pci->fc_dev,FC_SRAM_DEST_CAO   | FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_DMA2);
+
+       fc_pci->init_state |= FC_PCI_DMA_INIT;
+       goto success;
+dma1_free:
+       flexcop_dma_free(&fc_pci->dma[0]);
+
+success:
+       return ret;
+}
+
+static void flexcop_pci_dma_exit(struct flexcop_pci *fc_pci)
+{
+       if (fc_pci->init_state & FC_PCI_DMA_INIT) {
+               flexcop_dma_free(&fc_pci->dma[0]);
+               flexcop_dma_free(&fc_pci->dma[1]);
+       }
+       fc_pci->init_state &= ~FC_PCI_DMA_INIT;
+}
+
+static int flexcop_pci_init(struct flexcop_pci *fc_pci)
+{
+       int ret;
+       u8 card_rev;
+
+       pci_read_config_byte(fc_pci->pdev, PCI_CLASS_REVISION, &card_rev);
+       info("card revision %x", card_rev);
+
+       if ((ret = pci_enable_device(fc_pci->pdev)) != 0)
+               return ret;
+
+       pci_set_master(fc_pci->pdev);
+
+       /* enable interrupts */
+       // pci_write_config_dword(pdev, 0x6c, 0x8000);
+
+       if ((ret = pci_request_regions(fc_pci->pdev, DRIVER_NAME)) != 0)
+               goto err_pci_disable_device;
+
+       fc_pci->io_mem = pci_iomap(fc_pci->pdev, 0, 0x800);
+
+       if (!fc_pci->io_mem) {
+               err("cannot map io memory\n");
+               ret = -EIO;
+               goto err_pci_release_regions;
+       }
+
+       pci_set_drvdata(fc_pci->pdev, fc_pci);
+
+       if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_irq,
+                                       SA_SHIRQ, DRIVER_NAME, fc_pci)) != 0)
+               goto err_pci_iounmap;
+
+       spin_lock_init(&fc_pci->irq_lock);
+
+       fc_pci->init_state |= FC_PCI_INIT;
+       goto success;
+
+err_pci_iounmap:
+       pci_iounmap(fc_pci->pdev, fc_pci->io_mem);
+       pci_set_drvdata(fc_pci->pdev, NULL);
+err_pci_release_regions:
+       pci_release_regions(fc_pci->pdev);
+err_pci_disable_device:
+       pci_disable_device(fc_pci->pdev);
+
+success:
+       return ret;
+}
+
+static void flexcop_pci_exit(struct flexcop_pci *fc_pci)
+{
+       if (fc_pci->init_state & FC_PCI_INIT) {
+               free_irq(fc_pci->pdev->irq, fc_pci);
+               pci_iounmap(fc_pci->pdev, fc_pci->io_mem);
+               pci_set_drvdata(fc_pci->pdev, NULL);
+               pci_release_regions(fc_pci->pdev);
+               pci_disable_device(fc_pci->pdev);
+       }
+       fc_pci->init_state &= ~FC_PCI_INIT;
+}
+
+
+static int flexcop_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       struct flexcop_device *fc;
+       struct flexcop_pci *fc_pci;
+       int ret = -ENOMEM;
+
+       if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_pci))) == NULL) {
+               err("out of memory\n");
+               return -ENOMEM;
+       }
+
+/* general flexcop init */
+       fc_pci = fc->bus_specific;
+       fc_pci->fc_dev = fc;
+
+       fc->read_ibi_reg = flexcop_pci_read_ibi_reg;
+       fc->write_ibi_reg = flexcop_pci_write_ibi_reg;
+       fc->i2c_request = flexcop_i2c_request;
+       fc->get_mac_addr = flexcop_eeprom_check_mac_addr;
+
+       fc->stream_control = flexcop_pci_stream_control;
+
+       if (enable_pid_filtering)
+               info("will use the HW PID filter.");
+       else
+               info("will pass the complete TS to the demuxer.");
+
+       fc->pid_filtering = enable_pid_filtering;
+       fc->bus_type = FC_PCI;
+
+       fc->dev = &pdev->dev;
+       fc->owner = THIS_MODULE;
+
+/* bus specific part */
+       fc_pci->pdev = pdev;
+       if ((ret = flexcop_pci_init(fc_pci)) != 0)
+               goto err_kfree;
+
+/* init flexcop */
+       if ((ret = flexcop_device_initialize(fc)) != 0)
+               goto err_pci_exit;
+
+/* init dma */
+       if ((ret = flexcop_pci_dma_init(fc_pci)) != 0)
+               goto err_fc_exit;
+
+       goto success;
+err_fc_exit:
+       flexcop_device_exit(fc);
+err_pci_exit:
+       flexcop_pci_exit(fc_pci);
+err_kfree:
+       flexcop_device_kfree(fc);
+success:
+       return ret;
+}
+
+/* in theory every _exit function should be called exactly two times,
+ * here and in the bail-out-part of the _init-function
+ */
+static void flexcop_pci_remove(struct pci_dev *pdev)
+{
+       struct flexcop_pci *fc_pci = pci_get_drvdata(pdev);
+
+       flexcop_pci_dma_exit(fc_pci);
+       flexcop_device_exit(fc_pci->fc_dev);
+       flexcop_pci_exit(fc_pci);
+       flexcop_device_kfree(fc_pci->fc_dev);
+}
+
+static struct pci_device_id flexcop_pci_tbl[] = {
+       { PCI_DEVICE(0x13d0, 0x2103) },
+/*     { PCI_DEVICE(0x13d0, 0x2200) }, PCI FlexCopIII ? */
+       { },
+};
+
+MODULE_DEVICE_TABLE(pci, flexcop_pci_tbl);
+
+static struct pci_driver flexcop_pci_driver = {
+       .name = "Technisat/B2C2 FlexCop II/IIb/III PCI",
+       .id_table = flexcop_pci_tbl,
+       .probe = flexcop_pci_probe,
+       .remove = flexcop_pci_remove,
+};
+
+static int __init flexcop_pci_module_init(void)
+{
+       return pci_register_driver(&flexcop_pci_driver);
+}
+
+static void __exit flexcop_pci_module_exit(void)
+{
+       pci_unregister_driver(&flexcop_pci_driver);
+}
+
+module_init(flexcop_pci_module_init);
+module_exit(flexcop_pci_module_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_NAME);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/b2c2/flexcop-usb.c b/drivers/media/dvb/b2c2/flexcop-usb.c
new file mode 100644 (file)
index 0000000..0113449
--- /dev/null
@@ -0,0 +1,577 @@
+/*
+ * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+ *
+ * flexcop-usb.c - covers the USB part.
+ *
+ * see flexcop.c for copyright information.
+ */
+
+#define FC_LOG_PREFIX "flexcop_usb"
+#include "flexcop-usb.h"
+#include "flexcop-common.h"
+
+/* Version information */
+#define DRIVER_VERSION "0.1"
+#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver"
+#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de>"
+
+/* debug */
+#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
+#define dprintk(level,args...) \
+           do { if ((debug & level)) { printk(args); } } while (0)
+#define debug_dump(b,l,method) {\
+       int i; \
+       for (i = 0; i < l; i++) method("%02x ", b[i]); \
+       method("\n");\
+}
+
+#define DEBSTATUS ""
+#else
+#define dprintk(level,args...)
+#define debug_dump(b,l,method)
+#define DEBSTATUS " (debugging is not enabled)"
+#endif
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS);
+#undef DEBSTATUS
+
+#define deb_info(args...) dprintk(0x01,args)
+#define deb_ts(args...)   dprintk(0x02,args)
+#define deb_ctrl(args...) dprintk(0x04,args)
+#define deb_i2c(args...)  dprintk(0x08,args)
+#define deb_v8(args...)   dprintk(0x10,args)
+
+/* 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
+ * - 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 flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read)
+{
+       struct flexcop_usb *fc_usb = fc->bus_specific;
+       u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG;
+       u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR;
+       u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) | (read ? 0x80 : 0);
+
+       int len = usb_control_msg(fc_usb->udev,
+                       read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT,
+                       request,
+                       request_type,  /* 0xc0 read or 0x40 write*/
+                       wAddress,
+                       0,
+                       val,
+                       sizeof(u32),
+                       B2C2_WAIT_FOR_OPERATION_RDW * HZ);
+
+       if (len != sizeof(u32)) {
+               err("error while %s dword from %d (%d).",read ? "reading" : "writing",
+                       wAddress,wRegOffsPCI);
+               return -EIO;
+       }
+       return 0;
+}
+
+/*
+ * DKT 010817 - add support for V8 memory read/write and flash update
+ */
+static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb,
+               flexcop_usb_request_t req, u8 page, u16 wAddress,
+               u8 *pbBuffer,u32 buflen)
+{
+//     u8 dwRequestType;
+       u8 request_type = USB_TYPE_VENDOR;
+       u16 wIndex;
+       int nWaitTime,pipe,len;
+
+       wIndex = page << 8;
+
+       switch (req) {
+               case B2C2_USB_READ_V8_MEM:
+                       nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
+                       request_type |= USB_DIR_IN;
+//                     dwRequestType = (u8) RTYPE_READ_V8_MEMORY;
+                       pipe = B2C2_USB_CTRL_PIPE_IN;
+               break;
+               case B2C2_USB_WRITE_V8_MEM:
+                       wIndex |= pbBuffer[0];
+                       request_type |= USB_DIR_OUT;
+                       nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
+//                     dwRequestType = (u8) RTYPE_WRITE_V8_MEMORY;
+                       pipe = B2C2_USB_CTRL_PIPE_OUT;
+               break;
+               case B2C2_USB_FLASH_BLOCK:
+                       request_type |= USB_DIR_OUT;
+                       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;
+       }
+       deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n",request_type,req,
+                       wAddress,wIndex,buflen);
+
+       len = usb_control_msg(fc_usb->udev,pipe,
+                       req,
+                       request_type,
+                       wAddress,
+                       wIndex,
+                       pbBuffer,
+                       buflen,
+                       nWaitTime * HZ);
+
+       debug_dump(pbBuffer,len,deb_v8);
+
+       return len == buflen ? 0 : -EIO;
+}
+
+#define bytes_left_to_read_on_page(paddr,buflen) \
+                       ((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \
+                       ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)))
+
+static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,flexcop_usb_request_t req,
+               flexcop_usb_mem_page_t page_start, u32 addr, int extended, u8 *buf, u32 len)
+{
+       int i,ret = 0;
+       u16 wMax;
+       u32 pagechunk = 0;
+
+       switch(req) {
+               case B2C2_USB_READ_V8_MEM:  wMax = USB_MEM_READ_MAX; break;
+               case B2C2_USB_WRITE_V8_MEM:     wMax = USB_MEM_WRITE_MAX; break;
+               case B2C2_USB_FLASH_BLOCK:  wMax = USB_FLASH_MAX; break;
+               default:
+                       return -EINVAL;
+               break;
+       }
+       for (i = 0; i < len;) {
+               pagechunk = wMax < bytes_left_to_read_on_page(addr,len) ? wMax : bytes_left_to_read_on_page(addr,len);
+               deb_info("%x\n",(addr & V8_MEMORY_PAGE_MASK) | (V8_MEMORY_EXTENDED*extended));
+               if ((ret = flexcop_usb_v8_memory_req(fc_usb,req,
+                               page_start + (addr / V8_MEMORY_PAGE_SIZE), /* actual page */
+                               (addr & V8_MEMORY_PAGE_MASK) | (V8_MEMORY_EXTENDED*extended),
+                               &buf[i],pagechunk)) < 0)
+                       return ret;
+
+               addr += pagechunk;
+               len -= pagechunk;
+       }
+       return 0;
+}
+
+static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended)
+{
+       return flexcop_usb_memory_req(fc->bus_specific,B2C2_USB_READ_V8_MEM,
+                       V8_MEMORY_PAGE_FLASH,0x1f010,1,fc->dvb_adapter.proposed_mac,6);
+}
+
+#if 0
+static int flexcop_usb_utility_req(struct flexcop_usb *fc_usb, int set,
+               flexcop_usb_utility_function_t func, u8 extra, u16 wIndex,
+               u16 buflen, u8 *pvBuffer)
+{
+       u16 wValue;
+       u8 request_type = (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR;
+//     u8 dwRequestType = (u8) RTYPE_GENERIC,
+       int nWaitTime = 2,
+               pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN,
+               len;
+
+       wValue = (func << 8) | extra;
+
+       len = usb_control_msg(fc_usb->udev,pipe,
+                       B2C2_USB_UTILITY,
+                       request_type,
+                       wValue,
+                       wIndex,
+                       pvBuffer,
+                       buflen,
+                       nWaitTime * HZ);
+       return len == buflen ? 0 : -EIO;
+}
+#endif
+
+/* usb i2c stuff */
+static int flexcop_usb_i2c_req(struct flexcop_usb *fc_usb,
+               flexcop_usb_request_t req, flexcop_usb_i2c_function_t func,
+               flexcop_i2c_port_t port, u8 chipaddr, u8 addr, u8 *buf, u8 buflen)
+{
+       u16 wValue, wIndex;
+       int nWaitTime,pipe,len;
+//     u8 dwRequestType;
+       u8 request_type = USB_TYPE_VENDOR;
+
+       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;
+                       request_type |= USB_DIR_OUT;
+               break;
+               case USB_FUNC_I2C_READ:
+               case USB_FUNC_I2C_REPEATREAD:
+                       pipe = B2C2_USB_CTRL_PIPE_IN;
+                       nWaitTime = 2;
+//                     dwRequestType = (u8) RTYPE_GENERIC;
+                       request_type |= USB_DIR_IN;
+               break;
+               default:
+                       deb_info("unsupported function for i2c_req %x\n",func);
+                       return -EINVAL;
+       }
+       wValue = (func << 8 ) | (port << 4);
+       wIndex = (chipaddr << 8 ) | addr;
+
+       deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n",func,request_type,req,
+                       ((wValue && 0xff) << 8),wValue >> 8,((wIndex && 0xff) << 8),wIndex >> 8);
+
+       len = usb_control_msg(fc_usb->udev,pipe,
+                       req,
+                       request_type,
+                       wValue,
+                       wIndex,
+                       buf,
+                       buflen,
+                       nWaitTime * HZ);
+
+       return len == buflen ? 0 : -EREMOTEIO;
+}
+
+/* actual bus specific access functions, make sure prototype are/will be equal to pci */
+static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc, flexcop_ibi_register reg)
+{
+       flexcop_ibi_value val;
+       val.raw = 0;
+       flexcop_usb_readwrite_dw(fc,reg, &val.raw, 1);
+       return val;
+}
+
+static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc, flexcop_ibi_register reg, flexcop_ibi_value val)
+{
+       return flexcop_usb_readwrite_dw(fc,reg, &val.raw, 0);
+}
+
+static int flexcop_usb_i2c_request(struct flexcop_device *fc, flexcop_access_op_t op,
+               flexcop_i2c_port_t port, u8 chipaddr, u8 addr, u8 *buf, u16 len)
+{
+       if (op == FC_READ)
+               return flexcop_usb_i2c_req(fc->bus_specific,B2C2_USB_I2C_REQUEST,USB_FUNC_I2C_READ,port,chipaddr,addr,buf,len);
+       else
+               return flexcop_usb_i2c_req(fc->bus_specific,B2C2_USB_I2C_REQUEST,USB_FUNC_I2C_WRITE,port,chipaddr,addr,buf,len);
+}
+
+static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb, u8 *buffer, int buffer_length)
+{
+       u8 *b;
+       int l;
+
+       deb_ts("tmp_buffer_length=%d, buffer_length=%d\n", fc_usb->tmp_buffer_length, buffer_length);
+
+       if (fc_usb->tmp_buffer_length > 0) {
+               memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer, buffer_length);
+               fc_usb->tmp_buffer_length += buffer_length;
+               b = fc_usb->tmp_buffer;
+               l = fc_usb->tmp_buffer_length;
+       } else {
+               b=buffer;
+               l=buffer_length;
+       }
+
+       while (l >= 190) {
+               if (*b == 0xff)
+                       switch (*(b+1) & 0x03) {
+                               case 0x01: /* media packet */
+                                       if ( *(b+2) == 0x47 )
+                                               flexcop_pass_dmx_packets(fc_usb->fc_dev, b+2, 1);
+                                       else
+                                               deb_ts("not ts packet %02x %02x %02x %02x \n", *(b+2), *(b+3), *(b+4), *(b+5) );
+
+                                       b += 190;
+                                       l -= 190;
+                               break;
+                               default:
+                                       deb_ts("wrong packet type\n");
+                                       l = 0;
+                               break;
+                       }
+               else {
+                       deb_ts("wrong header\n");
+                       l = 0;
+               }
+       }
+
+       if (l>0)
+               memcpy(fc_usb->tmp_buffer, b, l);
+       fc_usb->tmp_buffer_length = l;
+}
+
+static void flexcop_usb_urb_complete(struct urb *urb, struct pt_regs *ptregs)
+{
+       struct flexcop_usb *fc_usb = urb->context;
+       int i;
+
+       if (urb->actual_length > 0)
+               deb_ts("urb completed, bufsize: %d actlen; %d\n",urb->transfer_buffer_length, urb->actual_length);
+
+       for (i = 0; i < urb->number_of_packets; i++) {
+               if (urb->iso_frame_desc[i].status < 0) {
+                       err("iso frame descriptor %d has an error: %d\n",i,urb->iso_frame_desc[i].status);
+               } else
+                       if (urb->iso_frame_desc[i].actual_length > 0) {
+                               deb_ts("passed %d bytes to the demux\n",urb->iso_frame_desc[i].actual_length);
+
+                               flexcop_usb_process_frame(fc_usb,
+                                       urb->transfer_buffer + urb->iso_frame_desc[i].offset,
+                                       urb->iso_frame_desc[i].actual_length);
+               }
+               urb->iso_frame_desc[i].status = 0;
+               urb->iso_frame_desc[i].actual_length = 0;
+       }
+
+       usb_submit_urb(urb,GFP_ATOMIC);
+}
+
+static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff)
+{
+       /* submit/kill iso packets */
+       return 0;
+}
+
+static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb)
+{
+       int i;
+       for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
+               if (fc_usb->iso_urb[i] != NULL) {
+                       deb_ts("unlinking/killing urb no. %d\n",i);
+                       usb_kill_urb(fc_usb->iso_urb[i]);
+                       usb_free_urb(fc_usb->iso_urb[i]);
+               }
+
+       if (fc_usb->iso_buffer != NULL)
+               pci_free_consistent(NULL,fc_usb->buffer_size, fc_usb->iso_buffer, fc_usb->dma_addr);
+}
+
+static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
+{
+       u16 frame_size = fc_usb->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_ts("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);
+
+       fc_usb->iso_buffer = pci_alloc_consistent(NULL,bufsize,&fc_usb->dma_addr);
+       if (fc_usb->iso_buffer == NULL)
+               return -ENOMEM;
+       memset(fc_usb->iso_buffer, 0, bufsize);
+       fc_usb->buffer_size = bufsize;
+
+       /* creating iso urbs */
+       for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
+               if (!(fc_usb->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++) {
+               int frame_offset = 0;
+               struct urb *urb = fc_usb->iso_urb[i];
+               deb_ts("initializing and submitting urb no. %d (buf_offset: %d).\n",i,buffer_offset);
+
+               urb->dev = fc_usb->udev;
+               urb->context = fc_usb;
+               urb->complete = flexcop_usb_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 = fc_usb->iso_buffer + buffer_offset;
+
+               buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
+               for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
+                       deb_ts("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(fc_usb->iso_urb[i],GFP_ATOMIC))) {
+                       err("submitting urb %d failed with %d.",i,ret);
+                       goto urb_error;
+               }
+               deb_ts("submitted urb no. %d.\n",i);
+       }
+
+/* SRAM */
+
+       flexcop_sram_set_dest(fc_usb->fc_dev,FC_SRAM_DEST_MEDIA | FC_SRAM_DEST_NET |
+                       FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_WAN_USB);
+       flexcop_wan_set_speed(fc_usb->fc_dev,FC_WAN_SPEED_8MBITS);
+       flexcop_sram_ctrl(fc_usb->fc_dev,1,1,1);
+
+       ret = 0;
+       goto success;
+urb_error:
+       flexcop_usb_transfer_exit(fc_usb);
+success:
+       return ret;
+}
+
+static int flexcop_usb_init(struct flexcop_usb *fc_usb)
+{
+       /* use the alternate setting with the larges buffer */
+       usb_set_interface(fc_usb->udev,0,1);
+       switch (fc_usb->udev->speed) {
+               case USB_SPEED_LOW:
+                       err("cannot handle USB speed because it is to sLOW.");
+                       return -ENODEV;
+                       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.");
+                       return -ENODEV;
+       }
+       usb_set_intfdata(fc_usb->uintf, fc_usb);
+       return 0;
+}
+
+static void flexcop_usb_exit(struct flexcop_usb *fc_usb)
+{
+       usb_set_intfdata(fc_usb->uintf, NULL);
+}
+
+static int flexcop_usb_probe(struct usb_interface *intf,
+               const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct flexcop_usb *fc_usb = NULL;
+       struct flexcop_device *fc = NULL;
+       int ret;
+
+       if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) {
+               err("out of memory\n");
+               return -ENOMEM;
+       }
+
+/* general flexcop init */
+       fc_usb = fc->bus_specific;
+       fc_usb->fc_dev = fc;
+
+       fc->read_ibi_reg  = flexcop_usb_read_ibi_reg;
+       fc->write_ibi_reg = flexcop_usb_write_ibi_reg;
+       fc->i2c_request = flexcop_usb_i2c_request;
+       fc->get_mac_addr = flexcop_usb_get_mac_addr;
+
+       fc->stream_control = flexcop_usb_stream_control;
+
+       fc->pid_filtering = 1;
+       fc->bus_type = FC_USB;
+
+       fc->dev = &udev->dev;
+       fc->owner = THIS_MODULE;
+
+/* bus specific part */
+       fc_usb->udev = udev;
+       fc_usb->uintf = intf;
+       if ((ret = flexcop_usb_init(fc_usb)) != 0)
+               goto err_kfree;
+
+/* init flexcop */
+       if ((ret = flexcop_device_initialize(fc)) != 0)
+               goto err_usb_exit;
+
+/* xfer init */
+       if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0)
+               goto err_fc_exit;
+
+       info("%s successfully initialized and connected.",DRIVER_NAME);
+       ret = 0;
+       goto success;
+err_fc_exit:
+       flexcop_device_exit(fc);
+err_usb_exit:
+       flexcop_usb_exit(fc_usb);
+err_kfree:
+       flexcop_device_kfree(fc);
+success:
+       return ret;
+}
+
+static void flexcop_usb_disconnect(struct usb_interface *intf)
+{
+       struct flexcop_usb *fc_usb = usb_get_intfdata(intf);
+       flexcop_usb_transfer_exit(fc_usb);
+       flexcop_device_exit(fc_usb->fc_dev);
+       flexcop_usb_exit(fc_usb);
+       flexcop_device_kfree(fc_usb->fc_dev);
+       info("%s successfully deinitialized and disconnected.",DRIVER_NAME);
+}
+
+static struct usb_device_id flexcop_usb_table [] = {
+           { USB_DEVICE(0x0af7, 0x0101) },
+           { }
+};
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver flexcop_usb_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "Technisat/B2C2 FlexCop II/IIb/III USB",
+       .probe          = flexcop_usb_probe,
+       .disconnect = flexcop_usb_disconnect,
+       .id_table       = flexcop_usb_table,
+};
+
+/* module stuff */
+static int __init flexcop_usb_module_init(void)
+{
+       int result;
+       if ((result = usb_register(&flexcop_usb_driver))) {
+               err("usb_register failed. (%d)",result);
+               return result;
+       }
+
+       return 0;
+}
+
+static void __exit flexcop_usb_module_exit(void)
+{
+       /* deregister this driver from the USB subsystem */
+       usb_deregister(&flexcop_usb_driver);
+}
+
+module_init(flexcop_usb_module_init);
+module_exit(flexcop_usb_module_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_NAME);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/b2c2/flexcop.c b/drivers/media/dvb/b2c2/flexcop.c
new file mode 100644 (file)
index 0000000..8b5d14d
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * flexcop.c - driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher <patrick.boettcher@desy.de>
+ *
+ * based on the skystar2-driver
+ * Copyright (C) 2003 Vadim Catana, skystar@moldova.cc
+ *
+ * Acknowledgements:
+ *     John Jurrius from BBTI, Inc. for extensive support with
+ *         code examples and data books
+ *
+ *     Bjarne Steinsbo, bjarne at steinsbo.com (some ideas for rewriting)
+ *
+ * Contributions to the skystar2-driver have been done by
+ *     Vincenzo Di Massa, hawk.it at tiscalinet.it (several DiSEqC fixes)
+ *     Roberto Ragusa, r.ragusa at libero.it (polishing, restyling the code)
+ *     Niklas Peinecke, peinecke at gdv.uni-hannover.de (hardware pid/mac filtering)
+ *
+ *
+ * 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 "flexcop.h"
+
+#define DRIVER_NAME "B2C2 FlexcopII/II(b)/III digital TV receiver chip"
+#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de"
+
+#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
+#define DEBSTATUS ""
+#else
+#define DEBSTATUS " (debugging is not enabled)"
+#endif
+
+int b2c2_flexcop_debug;
+module_param_named(debug, b2c2_flexcop_debug,  int, 0644);
+MODULE_PARM_DESC(debug, "set debug level (1=info,2=tuner,4=i2c,8=ts,16=sram (|-able))." DEBSTATUS);
+#undef DEBSTATUS
+
+/* global zero for ibi values */
+flexcop_ibi_value ibi_zero;
+
+static int flexcop_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+       struct flexcop_device *fc = dvbdmxfeed->demux->priv;
+       return flexcop_pid_feed_control(fc,dvbdmxfeed,1);
+}
+
+static int flexcop_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+       struct flexcop_device *fc = dvbdmxfeed->demux->priv;
+       return flexcop_pid_feed_control(fc,dvbdmxfeed,0);
+}
+
+static int flexcop_dvb_init(struct flexcop_device *fc)
+{
+       int ret;
+       if ((ret = dvb_register_adapter(&fc->dvb_adapter,"FlexCop Digital TV device",fc->owner)) < 0) {
+               err("error registering DVB adapter");
+               return ret;
+       }
+       fc->dvb_adapter.priv = fc;
+
+       fc->demux.dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
+       fc->demux.priv = fc;
+
+       fc->demux.filternum = fc->demux.feednum = FC_MAX_FEED;
+
+       fc->demux.start_feed = flexcop_dvb_start_feed;
+       fc->demux.stop_feed = flexcop_dvb_stop_feed;
+       fc->demux.write_to_decoder = NULL;
+
+       if ((ret = dvb_dmx_init(&fc->demux)) < 0) {
+               err("dvb_dmx failed: error %d",ret);
+               goto err_dmx;
+       }
+
+       fc->hw_frontend.source = DMX_FRONTEND_0;
+
+       fc->dmxdev.filternum = fc->demux.feednum;
+       fc->dmxdev.demux = &fc->demux.dmx;
+       fc->dmxdev.capabilities = 0;
+       if ((ret = dvb_dmxdev_init(&fc->dmxdev, &fc->dvb_adapter)) < 0) {
+               err("dvb_dmxdev_init failed: error %d",ret);
+               goto err_dmx_dev;
+       }
+
+       if ((ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->hw_frontend)) < 0) {
+               err("adding hw_frontend to dmx failed: error %d",ret);
+               goto err_dmx_add_hw_frontend;
+       }
+
+       fc->mem_frontend.source = DMX_MEMORY_FE;
+       if ((ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->mem_frontend)) < 0) {
+               err("adding mem_frontend to dmx failed: error %d",ret);
+               goto err_dmx_add_mem_frontend;
+       }
+
+       if ((ret = fc->demux.dmx.connect_frontend(&fc->demux.dmx, &fc->hw_frontend)) < 0) {
+               err("connect frontend failed: error %d",ret);
+               goto err_connect_frontend;
+       }
+
+       dvb_net_init(&fc->dvb_adapter, &fc->dvbnet, &fc->demux.dmx);
+
+       fc->init_state |= FC_STATE_DVB_INIT;
+       goto success;
+
+err_connect_frontend:
+       fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->mem_frontend);
+err_dmx_add_mem_frontend:
+       fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->hw_frontend);
+err_dmx_add_hw_frontend:
+       dvb_dmxdev_release(&fc->dmxdev);
+err_dmx_dev:
+       dvb_dmx_release(&fc->demux);
+err_dmx:
+       dvb_unregister_adapter(&fc->dvb_adapter);
+       return ret;
+
+success:
+       return 0;
+}
+
+static void flexcop_dvb_exit(struct flexcop_device *fc)
+{
+       if (fc->init_state & FC_STATE_DVB_INIT) {
+               dvb_net_release(&fc->dvbnet);
+
+               fc->demux.dmx.close(&fc->demux.dmx);
+               fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->mem_frontend);
+               fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->hw_frontend);
+               dvb_dmxdev_release(&fc->dmxdev);
+               dvb_dmx_release(&fc->demux);
+               dvb_unregister_adapter(&fc->dvb_adapter);
+
+               deb_info("deinitialized dvb stuff\n");
+       }
+       fc->init_state &= ~FC_STATE_DVB_INIT;
+}
+
+/* these methods are necessary to achieve the long-term-goal of hiding the
+ * struct flexcop_device from the bus-parts */
+void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len)
+{
+       dvb_dmx_swfilter(&fc->demux, buf, len);
+}
+EXPORT_SYMBOL(flexcop_pass_dmx_data);
+
+void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no)
+{
+       dvb_dmx_swfilter_packets(&fc->demux, buf, no);
+}
+EXPORT_SYMBOL(flexcop_pass_dmx_packets);
+
+static void flexcop_reset(struct flexcop_device *fc)
+{
+       flexcop_ibi_value v210,v204;
+
+/* reset the flexcop itself */
+       fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
+
+       v210.raw = 0;
+       v210.sw_reset_210.reset_blocks = 0xff;
+       v210.sw_reset_210.Block_reset_enable = 0xb2;
+       fc->write_ibi_reg(fc,sw_reset_210,v210);
+
+/* reset the periphical devices */
+
+       v204 = fc->read_ibi_reg(fc,misc_204);
+       v204.misc_204.Per_reset_sig = 0;
+       fc->write_ibi_reg(fc,misc_204,v204);
+       v204.misc_204.Per_reset_sig = 1;
+       fc->write_ibi_reg(fc,misc_204,v204);
+}
+
+struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len)
+{
+       void *bus;
+       struct flexcop_device *fc = kmalloc(sizeof(struct flexcop_device), GFP_KERNEL);
+       if (!fc) {
+               err("no memory");
+               return NULL;
+       }
+       memset(fc, 0, sizeof(struct flexcop_device));
+
+       bus = kmalloc(bus_specific_len, GFP_KERNEL);
+       if (!bus) {
+               err("no memory");
+               kfree(fc);
+               return NULL;
+       }
+       memset(bus, 0, bus_specific_len);
+
+       fc->bus_specific = bus;
+
+       return fc;
+}
+EXPORT_SYMBOL(flexcop_device_kmalloc);
+
+void flexcop_device_kfree(struct flexcop_device *fc)
+{
+       kfree(fc->bus_specific);
+       kfree(fc);
+}
+EXPORT_SYMBOL(flexcop_device_kfree);
+
+int flexcop_device_initialize(struct flexcop_device *fc)
+{
+       int ret;
+       ibi_zero.raw = 0;
+
+       flexcop_reset(fc);
+       flexcop_determine_revision(fc);
+       flexcop_sram_init(fc);
+       flexcop_hw_filter_init(fc);
+
+       flexcop_smc_ctrl(fc, 0);
+
+       if ((ret = flexcop_dvb_init(fc)))
+               goto error;
+
+       /* do the MAC address reading after initializing the dvb_adapter */
+       if (fc->get_mac_addr(fc, 0) == 0) {
+               u8 *b = fc->dvb_adapter.proposed_mac;
+               info("MAC address = %02x:%02x:%02x:%02x:%02x:%02x", b[0],b[1],b[2],b[3],b[4],b[5]);
+               flexcop_set_mac_filter(fc,b);
+               flexcop_mac_filter_ctrl(fc,1);
+       } else
+               warn("reading of MAC address failed.\n");
+
+
+       if ((ret = flexcop_i2c_init(fc)))
+               goto error;
+
+       if ((ret = flexcop_frontend_init(fc)))
+               goto error;
+
+       flexcop_device_name(fc,"initialization of","complete");
+
+       ret = 0;
+       goto success;
+error:
+       flexcop_device_exit(fc);
+success:
+       return ret;
+}
+EXPORT_SYMBOL(flexcop_device_initialize);
+
+void flexcop_device_exit(struct flexcop_device *fc)
+{
+       flexcop_frontend_exit(fc);
+       flexcop_i2c_exit(fc);
+       flexcop_dvb_exit(fc);
+}
+EXPORT_SYMBOL(flexcop_device_exit);
+
+static int flexcop_module_init(void)
+{
+       info(DRIVER_NAME " loaded successfully");
+       return 0;
+}
+
+static void flexcop_module_cleanup(void)
+{
+       info(DRIVER_NAME " unloaded successfully");
+}
+
+module_init(flexcop_module_init);
+module_exit(flexcop_module_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_NAME);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/bt8xx/dst_ca.c b/drivers/media/dvb/bt8xx/dst_ca.c
new file mode 100644 (file)
index 0000000..d781504
--- /dev/null
@@ -0,0 +1,861 @@
+/*
+       CA-driver for TwinHan DST Frontend/Card
+
+       Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include <linux/dvb/ca.h>
+#include "dvbdev.h"
+#include "dvb_frontend.h"
+
+#include "dst_ca.h"
+#include "dst_common.h"
+
+static unsigned int verbose = 1;
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)");
+
+static unsigned int debug = 1;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug messages, default is 1 (yes)");
+
+#define dprintk if (debug) printk
+
+/*     Need some more work     */
+static int ca_set_slot_descr(void)
+{
+       /*      We could make this more graceful ?      */
+       return -EOPNOTSUPP;
+}
+
+/*     Need some more work     */
+static int ca_set_pid(void)
+{
+       /*      We could make this more graceful ?      */
+       return -EOPNOTSUPP;
+}
+
+
+static int put_checksum(u8 *check_string, int length)
+{
+       u8 i = 0, checksum = 0;
+
+       if (verbose > 3) {
+               dprintk("%s: ========================= Checksum calculation ===========================\n", __FUNCTION__);
+               dprintk("%s: String Length=[0x%02x]\n", __FUNCTION__, length);
+
+               dprintk("%s: String=[", __FUNCTION__);
+       }
+       while (i < length) {
+               if (verbose > 3)
+                       dprintk(" %02x", check_string[i]);
+               checksum += check_string[i];
+               i++;
+       }
+       if (verbose > 3) {
+               dprintk(" ]\n");
+               dprintk("%s: Sum=[%02x]\n", __FUNCTION__, checksum);
+       }
+       check_string[length] = ~checksum + 1;
+       if (verbose > 3) {
+               dprintk("%s: Checksum=[%02x]\n", __FUNCTION__, check_string[length]);
+               dprintk("%s: ==========================================================================\n", __FUNCTION__);
+       }
+
+       return 0;
+}
+
+static int dst_ci_command(struct dst_state* state, u8 * data, u8 *ca_string, u8 len, int read)
+{
+       u8 reply;
+
+       dst_comm_init(state);
+       msleep(65);
+
+       if (write_dst(state, data, len)) {
+               dprintk("%s: Write not successful, trying to recover\n", __FUNCTION__);
+               dst_error_recovery(state);
+               return -1;
+       }
+
+       if ((dst_pio_disable(state)) < 0) {
+               dprintk("%s: DST PIO disable failed.\n", __FUNCTION__);
+               return -1;
+       }
+
+       if (read_dst(state, &reply, GET_ACK) < 0) {
+               dprintk("%s: Read not successful, trying to recover\n", __FUNCTION__);
+               dst_error_recovery(state);
+               return -1;
+       }
+
+       if (read) {
+               if (! dst_wait_dst_ready(state, LONG_DELAY)) {
+                       dprintk("%s: 8820 not ready\n", __FUNCTION__);
+                       return -1;
+               }
+
+               if (read_dst(state, ca_string, 128) < 0) {      /*      Try to make this dynamic        */
+                       dprintk("%s: Read not successful, trying to recover\n", __FUNCTION__);
+                       dst_error_recovery(state);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+static int dst_put_ci(struct dst_state *state, u8 *data, int len, u8 *ca_string, int read)
+{
+       u8 dst_ca_comm_err = 0;
+
+       while (dst_ca_comm_err < RETRIES) {
+               dst_comm_init(state);
+               if (verbose > 2)
+                       dprintk("%s: Put Command\n", __FUNCTION__);
+               if (dst_ci_command(state, data, ca_string, len, read)) {        // If error
+                       dst_error_recovery(state);
+                       dst_ca_comm_err++; // work required here.
+               }
+               break;
+       }
+
+       return 0;
+}
+
+
+
+static int ca_get_app_info(struct dst_state *state)
+{
+       static u8 command[8] = {0x07, 0x40, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff};
+
+       put_checksum(&command[0], command[0]);
+       if ((dst_put_ci(state, command, sizeof(command), state->messages, GET_REPLY)) < 0) {
+               dprintk("%s: -->dst_put_ci FAILED !\n", __FUNCTION__);
+               return -1;
+       }
+       if (verbose > 1) {
+               dprintk("%s: -->dst_put_ci SUCCESS !\n", __FUNCTION__);
+
+               dprintk("%s: ================================ CI Module Application Info ======================================\n", __FUNCTION__);
+               dprintk("%s: Application Type=[%d], Application Vendor=[%d], Vendor Code=[%d]\n%s: Application info=[%s]\n",
+                       __FUNCTION__, state->messages[7], (state->messages[8] << 8) | state->messages[9],
+                       (state->messages[10] << 8) | state->messages[11], __FUNCTION__, (char *)(&state->messages[12]));
+               dprintk("%s: ==================================================================================================\n", __FUNCTION__);
+       }
+
+       return 0;
+}
+
+static int ca_get_slot_caps(struct dst_state *state, struct ca_caps *p_ca_caps, void *arg)
+{
+       int i;
+       u8 slot_cap[256];
+       static u8 slot_command[8] = {0x07, 0x40, 0x02, 0x00, 0x02, 0x00, 0x00, 0xff};
+
+       put_checksum(&slot_command[0], slot_command[0]);
+       if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_cap, GET_REPLY)) < 0) {
+               dprintk("%s: -->dst_put_ci FAILED !\n", __FUNCTION__);
+               return -1;
+       }
+       if (verbose > 1)
+               dprintk("%s: -->dst_put_ci SUCCESS !\n", __FUNCTION__);
+
+       /*      Will implement the rest soon            */
+
+       if (verbose > 1) {
+               dprintk("%s: Slot cap = [%d]\n", __FUNCTION__, slot_cap[7]);
+               dprintk("===================================\n");
+               for (i = 0; i < 8; i++)
+                       dprintk(" %d", slot_cap[i]);
+               dprintk("\n");
+       }
+
+       p_ca_caps->slot_num = 1;
+       p_ca_caps->slot_type = 1;
+       p_ca_caps->descr_num = slot_cap[7];
+       p_ca_caps->descr_type = 1;
+
+
+       if (copy_to_user((struct ca_caps *)arg, p_ca_caps, sizeof (struct ca_caps))) {
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+/*     Need some more work     */
+static int ca_get_slot_descr(struct dst_state *state, struct ca_msg *p_ca_message, void *arg)
+{
+       return -EOPNOTSUPP;
+}
+
+
+static int ca_get_slot_info(struct dst_state *state, struct ca_slot_info *p_ca_slot_info, void *arg)
+{
+       int i;
+       static u8 slot_command[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff};
+
+       u8 *slot_info = state->rxbuffer;
+
+       put_checksum(&slot_command[0], 7);
+       if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_info, GET_REPLY)) < 0) {
+               dprintk("%s: -->dst_put_ci FAILED !\n", __FUNCTION__);
+               return -1;
+       }
+       if (verbose > 1)
+               dprintk("%s: -->dst_put_ci SUCCESS !\n", __FUNCTION__);
+
+       /*      Will implement the rest soon            */
+
+       if (verbose > 1) {
+               dprintk("%s: Slot info = [%d]\n", __FUNCTION__, slot_info[3]);
+               dprintk("===================================\n");
+               for (i = 0; i < 8; i++)
+                       dprintk(" %d", slot_info[i]);
+               dprintk("\n");
+       }
+
+       if (slot_info[4] & 0x80) {
+               p_ca_slot_info->flags = CA_CI_MODULE_PRESENT;
+               p_ca_slot_info->num = 1;
+               p_ca_slot_info->type = CA_CI;
+       }
+       else if (slot_info[4] & 0x40) {
+               p_ca_slot_info->flags = CA_CI_MODULE_READY;
+               p_ca_slot_info->num = 1;
+               p_ca_slot_info->type = CA_CI;
+       }
+       else {
+               p_ca_slot_info->flags = 0;
+       }
+
+       if (copy_to_user((struct ca_slot_info *)arg, p_ca_slot_info, sizeof (struct ca_slot_info))) {
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+
+
+
+static int ca_get_message(struct dst_state *state, struct ca_msg *p_ca_message, void *arg)
+{
+       u8 i = 0;
+       u32 command = 0;
+
+       if (copy_from_user(p_ca_message, (void *)arg, sizeof (struct ca_msg)))
+               return -EFAULT;
+
+
+       if (p_ca_message->msg) {
+               if (verbose > 3)
+                       dprintk("Message = [%02x %02x %02x]\n", p_ca_message->msg[0], p_ca_message->msg[1], p_ca_message->msg[2]);
+
+               for (i = 0; i < 3; i++) {
+                       command = command | p_ca_message->msg[i];
+                       if (i < 2)
+                               command = command << 8;
+               }
+               if (verbose > 3)
+                       dprintk("%s:Command=[0x%x]\n", __FUNCTION__, command);
+
+               switch (command) {
+                       case CA_APP_INFO:
+                               memcpy(p_ca_message->msg, state->messages, 128);
+                               if (copy_to_user((void *)arg, p_ca_message, sizeof (struct ca_msg)) )
+                                       return -EFAULT;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int handle_en50221_tag(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer)
+{
+       if (state->dst_hw_cap & DST_TYPE_HAS_SESSION) {
+               hw_buffer->msg[2] = p_ca_message->msg[1];               /*              MSB                     */
+               hw_buffer->msg[3] = p_ca_message->msg[2];               /*              LSB                     */
+       }
+       else {
+               hw_buffer->msg[2] = 0x03;
+               hw_buffer->msg[3] = 0x00;
+       }
+       return 0;
+}
+
+static int debug_8820_buffer(struct ca_msg *hw_buffer)
+{
+       unsigned int i;
+
+       dprintk("%s:Debug=[", __FUNCTION__);
+       for (i = 0; i < (hw_buffer->msg[0] + 1); i++)
+               dprintk(" %02x", hw_buffer->msg[i]);
+       dprintk("]\n");
+
+       return 0;
+}
+
+static int write_to_8820(struct dst_state *state, struct ca_msg *hw_buffer, u8 reply)
+{
+       if ((dst_put_ci(state, hw_buffer->msg, (hw_buffer->length + 1), hw_buffer->msg, reply)) < 0) {
+               dprintk("%s: DST-CI Command failed.\n", __FUNCTION__);
+               dprintk("%s: Resetting DST.\n", __FUNCTION__);
+               rdc_reset_state(state);
+               return -1;
+       }
+       if (verbose > 2)
+               dprintk("%s: DST-CI Command succes.\n", __FUNCTION__);
+
+       return 0;
+}
+
+
+static int ca_set_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u8 reply, u8 query)
+{
+       u32 hw_offset, buf_offset, i, k;
+       u32 program_info_length = 0, es_info_length = 0, length = 0, words = 0;
+       u8 found_prog_ca_desc = 0, found_stream_ca_desc = 0, error_condition = 0, hw_buffer_length = 0;
+
+       if (verbose > 3)
+               dprintk("%s, p_ca_message length %d (0x%x)\n", __FUNCTION__,p_ca_message->length,p_ca_message->length );
+
+       handle_en50221_tag(state, p_ca_message, hw_buffer);                     /*      EN50221 tag             */
+
+       /*      Handle the length field (variable)      */
+       if (!(p_ca_message->msg[3] & 0x80)) {                           /*      Length = 1              */
+               length = p_ca_message->msg[3] & 0x7f;
+               words = 0;                                              /*      domi's suggestion       */
+       }
+       else {                                                          /*      Length = words          */
+               words = p_ca_message->msg[3] & 0x7f;
+               for (i = 0; i < words; i++) {
+                       length = length << 8;
+                       length = length | p_ca_message->msg[4 + i];
+               }
+       }
+       if (verbose > 4) {
+               dprintk("%s:Length=[%d (0x%x)], Words=[%d]\n", __FUNCTION__, length,length, words);
+
+               /*      Debug Input string              */
+               for (i = 0; i < length; i++)
+                       dprintk(" %02x", p_ca_message->msg[i]);
+               dprintk("]\n");
+       }
+
+       hw_offset = 7;
+       buf_offset = words + 4;
+
+       /*              Program Header                  */
+       if (verbose > 4)
+               dprintk("\n%s:Program Header=[", __FUNCTION__);
+       for (i = 0; i < 6; i++) {
+               hw_buffer->msg[hw_offset] = p_ca_message->msg[buf_offset];
+               if (verbose > 4)
+                       dprintk(" %02x", p_ca_message->msg[buf_offset]);
+               hw_offset++, buf_offset++, hw_buffer_length++;
+       }
+       if (verbose > 4)
+               dprintk("]\n");
+
+       program_info_length = 0;
+       program_info_length = (((program_info_length | p_ca_message->msg[words + 8]) & 0x0f) << 8) | p_ca_message->msg[words + 9];
+       if (verbose > 4)
+               dprintk("%s:Program info Length=[%d][%02x], hw_offset=[%d], buf_offset=[%d] \n",
+                       __FUNCTION__, program_info_length, program_info_length, hw_offset, buf_offset);
+
+       if (program_info_length && (program_info_length < 256)) {       /*      If program_info_length          */
+               hw_buffer->msg[11] = hw_buffer->msg[11] & 0x0f;         /*      req only 4 bits                 */
+               hw_buffer->msg[12] = hw_buffer->msg[12] + 1;            /*      increment! ASIC bug!            */
+
+               if (p_ca_message->msg[buf_offset + 1] == 0x09) {        /*      Check CA descriptor             */
+                       found_prog_ca_desc = 1;
+                       if (verbose > 4)
+                               dprintk("%s: Found CA descriptor @ Program level\n", __FUNCTION__);
+               }
+
+               if (found_prog_ca_desc) {                               /*      Command only if CA descriptor   */
+                       hw_buffer->msg[13] = p_ca_message->msg[buf_offset];     /*      CA PMT command ID       */
+                       hw_offset++, buf_offset++, hw_buffer_length++;
+               }
+
+               /*                      Program descriptors                             */
+               if (verbose > 4) {
+                       dprintk("%s:**********>buf_offset=[%d], hw_offset=[%d]\n", __FUNCTION__, buf_offset, hw_offset);
+                       dprintk("%s:Program descriptors=[", __FUNCTION__);
+               }
+               while (program_info_length && !error_condition) {               /*      Copy prog descriptors   */
+                       if (program_info_length > p_ca_message->length) {       /*      Error situation         */
+                               dprintk ("%s:\"WARNING\" Length error, line=[%d], prog_info_length=[%d]\n",
+                                                               __FUNCTION__, __LINE__, program_info_length);
+                               dprintk("%s:\"WARNING\" Bailing out of possible loop\n", __FUNCTION__);
+                               error_condition = 1;
+                               break;
+                       }
+
+                       hw_buffer->msg[hw_offset] = p_ca_message->msg[buf_offset];
+                       dprintk(" %02x", p_ca_message->msg[buf_offset]);
+                       hw_offset++, buf_offset++, hw_buffer_length++, program_info_length--;
+               }
+               if (verbose > 4) {
+                       dprintk("]\n");
+                       dprintk("%s:**********>buf_offset=[%d], hw_offset=[%d]\n", __FUNCTION__, buf_offset, hw_offset);
+               }
+               if (found_prog_ca_desc) {
+                       if (!reply) {
+                               hw_buffer->msg[13] = 0x01;              /*      OK descrambling                 */
+                               if (verbose > 1)
+                                       dprintk("CA PMT Command = OK Descrambling\n");
+                       }
+                       else {
+                               hw_buffer->msg[13] = 0x02;              /*      Ok MMI                          */
+                               if (verbose > 1)
+                                       dprintk("CA PMT Command = Ok MMI\n");
+                       }
+                       if (query) {
+                               hw_buffer->msg[13] = 0x03;              /*      Query                           */
+                               if (verbose > 1)
+                                       dprintk("CA PMT Command = CA PMT query\n");
+                       }
+               }
+       }
+       else {
+               hw_buffer->msg[11] = hw_buffer->msg[11] & 0xf0;         /*      Don't write to ASIC             */
+               hw_buffer->msg[12] = hw_buffer->msg[12] = 0x00;
+       }
+       if (verbose > 4)
+               dprintk("%s:**********>p_ca_message->length=[%d], buf_offset=[%d], hw_offset=[%d]\n",
+                                       __FUNCTION__, p_ca_message->length, buf_offset, hw_offset);
+
+       while ((buf_offset  < p_ca_message->length)  && !error_condition) {
+               /*      Bail out in case of an indefinite loop          */
+               if ((es_info_length > p_ca_message->length) || (buf_offset > p_ca_message->length)) {
+                       dprintk("%s:\"WARNING\" Length error, line=[%d], prog_info_length=[%d], buf_offset=[%d]\n",
+                                                       __FUNCTION__, __LINE__, program_info_length, buf_offset);
+
+                       dprintk("%s:\"WARNING\" Bailing out of possible loop\n", __FUNCTION__);
+                       error_condition = 1;
+                       break;
+               }
+
+               /*              Stream Header                           */
+
+               for (k = 0; k < 5; k++) {
+                       hw_buffer->msg[hw_offset + k] = p_ca_message->msg[buf_offset + k];
+               }
+
+               es_info_length = 0;
+               es_info_length = (es_info_length | (p_ca_message->msg[buf_offset + 3] & 0x0f)) << 8 | p_ca_message->msg[buf_offset + 4];
+
+               if (verbose > 4) {
+                       dprintk("\n%s:----->Stream header=[%02x %02x %02x %02x %02x]\n", __FUNCTION__,
+                               p_ca_message->msg[buf_offset + 0], p_ca_message->msg[buf_offset + 1],
+                               p_ca_message->msg[buf_offset + 2], p_ca_message->msg[buf_offset + 3],
+                               p_ca_message->msg[buf_offset + 4]);
+
+                       dprintk("%s:----->Stream type=[%02x], es length=[%d (0x%x)], Chars=[%02x] [%02x], buf_offset=[%d]\n", __FUNCTION__,
+                               p_ca_message->msg[buf_offset + 0], es_info_length, es_info_length,
+                               p_ca_message->msg[buf_offset + 3], p_ca_message->msg[buf_offset + 4], buf_offset);
+               }
+
+               hw_buffer->msg[hw_offset + 3] &= 0x0f;                  /*      req only 4 bits                 */
+
+               if (found_prog_ca_desc) {
+                       hw_buffer->msg[hw_offset + 3] = 0x00;
+                       hw_buffer->msg[hw_offset + 4] = 0x00;
+               }
+
+               hw_offset += 5, buf_offset += 5, hw_buffer_length += 5;
+
+               /*              Check for CA descriptor                 */
+               if (p_ca_message->msg[buf_offset + 1] == 0x09) {
+                       if (verbose > 4)
+                               dprintk("%s:Found CA descriptor @ Stream level\n", __FUNCTION__);
+                       found_stream_ca_desc = 1;
+               }
+
+               /*              ES descriptors                          */
+
+               if (es_info_length && !error_condition && !found_prog_ca_desc && found_stream_ca_desc) {
+//                     if (!ca_pmt_done) {
+                               hw_buffer->msg[hw_offset] = p_ca_message->msg[buf_offset];      /*      CA PMT cmd(es)  */
+                               if (verbose > 4)
+                                       printk("%s:----->CA PMT Command ID=[%02x]\n", __FUNCTION__, p_ca_message->msg[buf_offset]);
+//                             hw_offset++, buf_offset++, hw_buffer_length++, es_info_length--, ca_pmt_done = 1;
+                               hw_offset++, buf_offset++, hw_buffer_length++, es_info_length--;
+//                     }
+                       if (verbose > 4)
+                               dprintk("%s:----->ES descriptors=[", __FUNCTION__);
+
+                       while (es_info_length && !error_condition) {    /*      ES descriptors                  */
+                               if ((es_info_length > p_ca_message->length) || (buf_offset > p_ca_message->length)) {
+                                       if (verbose > 4) {
+                                               dprintk("%s:\"WARNING\" ES Length error, line=[%d], es_info_length=[%d], buf_offset=[%d]\n",
+                                                                               __FUNCTION__, __LINE__, es_info_length, buf_offset);
+
+                                               dprintk("%s:\"WARNING\" Bailing out of possible loop\n", __FUNCTION__);
+                                       }
+                                       error_condition = 1;
+                                       break;
+                               }
+
+                               hw_buffer->msg[hw_offset] = p_ca_message->msg[buf_offset];
+                               if (verbose > 3)
+                                       dprintk("%02x ", hw_buffer->msg[hw_offset]);
+                               hw_offset++, buf_offset++, hw_buffer_length++, es_info_length--;
+                       }
+                       found_stream_ca_desc = 0;                       /*      unset for new streams           */
+                       dprintk("]\n");
+               }
+       }
+
+       /*              MCU Magic words                                 */
+
+       hw_buffer_length += 7;
+       hw_buffer->msg[0] = hw_buffer_length;
+       hw_buffer->msg[1] = 64;
+       hw_buffer->msg[4] = 3;
+       hw_buffer->msg[5] = hw_buffer->msg[0] - 7;
+       hw_buffer->msg[6] = 0;
+
+
+       /*      Fix length      */
+       hw_buffer->length = hw_buffer->msg[0];
+
+       put_checksum(&hw_buffer->msg[0], hw_buffer->msg[0]);
+       /*      Do the actual write     */
+       if (verbose > 4) {
+               dprintk("%s:======================DEBUGGING================================\n", __FUNCTION__);
+               dprintk("%s: Actual Length=[%d]\n", __FUNCTION__, hw_buffer_length);
+       }
+       /*      Only for debugging!     */
+       if (verbose > 2)
+               debug_8820_buffer(hw_buffer);
+       if (verbose > 3)
+               dprintk("%s: Reply = [%d]\n", __FUNCTION__, reply);
+       write_to_8820(state, hw_buffer, reply);
+
+       return 0;
+}
+
+/*     Board supports CA PMT reply ?           */
+static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer)
+{
+       int ca_pmt_reply_test = 0;
+
+       /*      Do test board                   */
+       /*      Not there yet but soon          */
+
+
+       /*      CA PMT Reply capable            */
+       if (ca_pmt_reply_test) {
+               if ((ca_set_pmt(state, p_ca_message, hw_buffer, 1, GET_REPLY)) < 0) {
+                       dprintk("%s: ca_set_pmt.. failed !\n", __FUNCTION__);
+                       return -1;
+               }
+
+       /*      Process CA PMT Reply            */
+       /*      will implement soon             */
+               dprintk("%s: Not there yet\n", __FUNCTION__);
+       }
+       /*      CA PMT Reply not capable        */
+       if (!ca_pmt_reply_test) {
+               if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, NO_REPLY)) < 0) {
+                       dprintk("%s: ca_set_pmt.. failed !\n", __FUNCTION__);
+                       return -1;
+               }
+               if (verbose > 3)
+                       dprintk("%s: ca_set_pmt.. success !\n", __FUNCTION__);
+       /*      put a dummy message             */
+
+       }
+       return 0;
+}
+
+static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void *arg)
+{
+       int i = 0;
+       unsigned int ca_message_header_len;
+
+       u32 command = 0;
+       struct ca_msg *hw_buffer;
+
+       if ((hw_buffer = (struct ca_msg *) kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) {
+               printk("%s: Memory allocation failure\n", __FUNCTION__);
+               return -ENOMEM;
+       }
+       if (verbose > 3)
+               dprintk("%s\n", __FUNCTION__);
+
+       if (copy_from_user(p_ca_message, (void *)arg, sizeof (struct ca_msg)))
+               return -EFAULT;
+
+       if (p_ca_message->msg) {
+               ca_message_header_len = p_ca_message->length;   /*      Restore it back when you are done       */
+               /*      EN50221 tag     */
+               command = 0;
+
+               for (i = 0; i < 3; i++) {
+                       command = command | p_ca_message->msg[i];
+                       if (i < 2)
+                               command = command << 8;
+               }
+               if (verbose > 3)
+                       dprintk("%s:Command=[0x%x]\n", __FUNCTION__, command);
+
+               switch (command) {
+                       case CA_PMT:
+                               if (verbose > 3)
+                                       dprintk("Command = SEND_CA_PMT\n");
+                               if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) {
+                                       dprintk("%s: -->CA_PMT Failed !\n", __FUNCTION__);
+                                       return -1;
+                               }
+                               if (verbose > 3)
+                                       dprintk("%s: -->CA_PMT Success !\n", __FUNCTION__);
+//                             retval = dummy_set_pmt(state, p_ca_message, hw_buffer, 0, 0);
+
+                               break;
+
+                       case CA_PMT_REPLY:
+                               if (verbose > 3)
+                                       dprintk("Command = CA_PMT_REPLY\n");
+                               /*      Have to handle the 2 basic types of cards here  */
+                               if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) {
+                                       dprintk("%s: -->CA_PMT_REPLY Failed !\n", __FUNCTION__);
+                                       return -1;
+                               }
+                               if (verbose > 3)
+                                       dprintk("%s: -->CA_PMT_REPLY Success !\n", __FUNCTION__);
+
+                               /*      Certain boards do behave different ?            */
+//                             retval = ca_set_pmt(state, p_ca_message, hw_buffer, 1, 1);
+
+                       case CA_APP_INFO_ENQUIRY:               // only for debugging
+                               if (verbose > 3)
+                                       dprintk("%s: Getting Cam Application information\n", __FUNCTION__);
+
+                               if ((ca_get_app_info(state)) < 0) {
+                                       dprintk("%s: -->CA_APP_INFO_ENQUIRY Failed !\n", __FUNCTION__);
+                                       return -1;
+                               }
+                               if (verbose > 3)
+                                       printk("%s: -->CA_APP_INFO_ENQUIRY Success !\n", __FUNCTION__);
+
+                               break;
+               }
+       }
+       return 0;
+}
+
+static int dst_ca_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg)
+{
+       struct dvb_device* dvbdev = (struct dvb_device*) file->private_data;
+       struct dst_state* state = (struct dst_state*) dvbdev->priv;
+       struct ca_slot_info *p_ca_slot_info;
+       struct ca_caps *p_ca_caps;
+       struct ca_msg *p_ca_message;
+
+       if ((p_ca_message = (struct ca_msg *) kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) {
+               printk("%s: Memory allocation failure\n", __FUNCTION__);
+               return -ENOMEM;
+       }
+
+       if ((p_ca_slot_info = (struct ca_slot_info *) kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL)) == NULL) {
+               printk("%s: Memory allocation failure\n", __FUNCTION__);
+               return -ENOMEM;
+       }
+
+       if ((p_ca_caps = (struct ca_caps *) kmalloc(sizeof (struct ca_caps), GFP_KERNEL)) == NULL) {
+               printk("%s: Memory allocation failure\n", __FUNCTION__);
+               return -ENOMEM;
+       }
+
+       /*      We have now only the standard ioctl's, the driver is upposed to handle internals.       */
+       switch (cmd) {
+               case CA_SEND_MSG:
+                       if (verbose > 1)
+                               dprintk("%s: Sending message\n", __FUNCTION__);
+                       if ((ca_send_message(state, p_ca_message, arg)) < 0) {
+                               dprintk("%s: -->CA_SEND_MSG Failed !\n", __FUNCTION__);
+                               return -1;
+                       }
+
+                       break;
+
+               case CA_GET_MSG:
+                       if (verbose > 1)
+                               dprintk("%s: Getting message\n", __FUNCTION__);
+                       if ((ca_get_message(state, p_ca_message, arg)) < 0) {
+                               dprintk("%s: -->CA_GET_MSG Failed !\n", __FUNCTION__);
+                               return -1;
+                       }
+                       if (verbose > 1)
+                               dprintk("%s: -->CA_GET_MSG Success !\n", __FUNCTION__);
+
+                       break;
+
+               case CA_RESET:
+                       if (verbose > 1)
+                               dprintk("%s: Resetting DST\n", __FUNCTION__);
+                       dst_error_bailout(state);
+                       msleep(4000);
+
+                       break;
+
+               case CA_GET_SLOT_INFO:
+                       if (verbose > 1)
+                               dprintk("%s: Getting Slot info\n", __FUNCTION__);
+                       if ((ca_get_slot_info(state, p_ca_slot_info, arg)) < 0) {
+                               dprintk("%s: -->CA_GET_SLOT_INFO Failed !\n", __FUNCTION__);
+                               return -1;
+                       }
+                       if (verbose > 1)
+                               dprintk("%s: -->CA_GET_SLOT_INFO Success !\n", __FUNCTION__);
+
+                       break;
+
+               case CA_GET_CAP:
+                       if (verbose > 1)
+                               dprintk("%s: Getting Slot capabilities\n", __FUNCTION__);
+                       if ((ca_get_slot_caps(state, p_ca_caps, arg)) < 0) {
+                               dprintk("%s: -->CA_GET_CAP Failed !\n", __FUNCTION__);
+                               return -1;
+                       }
+                       if (verbose > 1)
+                               dprintk("%s: -->CA_GET_CAP Success !\n", __FUNCTION__);
+
+                       break;
+
+               case CA_GET_DESCR_INFO:
+                       if (verbose > 1)
+                               dprintk("%s: Getting descrambler description\n", __FUNCTION__);
+                       if ((ca_get_slot_descr(state, p_ca_message, arg)) < 0) {
+                               dprintk("%s: -->CA_GET_DESCR_INFO Failed !\n", __FUNCTION__);
+                               return -1;
+                       }
+                       if (verbose > 1)
+                               dprintk("%s: -->CA_GET_DESCR_INFO Success !\n", __FUNCTION__);
+
+                       break;
+
+               case CA_SET_DESCR:
+                       if (verbose > 1)
+                               dprintk("%s: Setting descrambler\n", __FUNCTION__);
+                       if ((ca_set_slot_descr()) < 0) {
+                               dprintk("%s: -->CA_SET_DESCR Failed !\n", __FUNCTION__);
+                               return -1;
+                       }
+                       if (verbose > 1)
+                               dprintk("%s: -->CA_SET_DESCR Success !\n", __FUNCTION__);
+
+                       break;
+
+               case CA_SET_PID:
+                       if (verbose > 1)
+                               dprintk("%s: Setting PID\n", __FUNCTION__);
+                       if ((ca_set_pid()) < 0) {
+                               dprintk("%s: -->CA_SET_PID Failed !\n", __FUNCTION__);
+                               return -1;
+                       }
+                       if (verbose > 1)
+                               dprintk("%s: -->CA_SET_PID Success !\n", __FUNCTION__);
+
+               default:
+                       return -EOPNOTSUPP;
+               };
+
+       return 0;
+}
+
+static int dst_ca_open(struct inode *inode, struct file *file)
+{
+       if (verbose > 4)
+               dprintk("%s:Device opened [%p]\n", __FUNCTION__, file);
+       try_module_get(THIS_MODULE);
+
+       return 0;
+}
+
+static int dst_ca_release(struct inode *inode, struct file *file)
+{
+       if (verbose > 4)
+               dprintk("%s:Device closed.\n", __FUNCTION__);
+       module_put(THIS_MODULE);
+
+       return 0;
+}
+
+static int dst_ca_read(struct file *file, char __user * buffer, size_t length, loff_t * offset)
+{
+       int bytes_read = 0;
+
+       if (verbose > 4)
+               dprintk("%s:Device read.\n", __FUNCTION__);
+
+       return bytes_read;
+}
+
+static int dst_ca_write(struct file *file, const char __user * buffer, size_t length, loff_t * offset)
+{
+       if (verbose > 4)
+               dprintk("%s:Device write.\n", __FUNCTION__);
+
+       return 0;
+}
+
+static struct file_operations dst_ca_fops = {
+       .owner = THIS_MODULE,
+       .ioctl = (void *)dst_ca_ioctl,
+       .open = dst_ca_open,
+       .release = dst_ca_release,
+       .read = dst_ca_read,
+       .write = dst_ca_write
+};
+
+static struct dvb_device dvbdev_ca = {
+       .priv = NULL,
+       .users = 1,
+       .readers = 1,
+       .writers = 1,
+       .fops = &dst_ca_fops
+};
+
+int dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter)
+{
+       struct dvb_device *dvbdev;
+       if (verbose > 4)
+               dprintk("%s:registering DST-CA device\n", __FUNCTION__);
+       dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA);
+       return 0;
+}
+
+EXPORT_SYMBOL(dst_ca_attach);
+
+MODULE_DESCRIPTION("DST DVB-S/T/C Combo CA driver");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/bt8xx/dst_common.h b/drivers/media/dvb/bt8xx/dst_common.h
new file mode 100644 (file)
index 0000000..0b3da29
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+       Frontend-driver for TwinHan DST Frontend
+
+       Copyright (C) 2003 Jamie Honan
+       Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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 DST_COMMON_H
+#define DST_COMMON_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/device.h>
+#include "bt878.h"
+
+#include "dst_ca.h"
+
+
+#define NO_DELAY               0
+#define LONG_DELAY             1
+#define DEVICE_INIT            2
+
+#define DELAY                  1
+
+#define DST_TYPE_IS_SAT                0
+#define DST_TYPE_IS_TERR       1
+#define DST_TYPE_IS_CABLE      2
+#define DST_TYPE_IS_ATSC       3
+
+#define DST_TYPE_HAS_NEWTUNE   1
+#define DST_TYPE_HAS_TS204     2
+#define DST_TYPE_HAS_SYMDIV    4
+#define DST_TYPE_HAS_FW_1      8
+#define DST_TYPE_HAS_FW_2      16
+#define DST_TYPE_HAS_FW_3      32
+#define DST_TYPE_HAS_FW_BUILD  64
+
+/*     Card capability list    */
+
+#define DST_TYPE_HAS_MAC       1
+#define DST_TYPE_HAS_DISEQC3   2
+#define DST_TYPE_HAS_DISEQC4   4
+#define DST_TYPE_HAS_DISEQC5   8
+#define DST_TYPE_HAS_MOTO      16
+#define DST_TYPE_HAS_CA                32
+#define        DST_TYPE_HAS_ANALOG     64      /*      Analog inputs   */
+#define DST_TYPE_HAS_SESSION   128
+
+
+#define RDC_8820_PIO_0_DISABLE 0
+#define RDC_8820_PIO_0_ENABLE  1
+#define RDC_8820_INT           2
+#define RDC_8820_RESET         4
+
+/*     DST Communication       */
+#define GET_REPLY              1
+#define NO_REPLY               0
+
+#define GET_ACK                        1
+#define FIXED_COMM             8
+
+#define ACK                    0xff
+
+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 ASIC 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;
+       u32 dst_hw_cap;
+       u8 dst_fw_version;
+       fe_sec_mini_cmd_t minicmd;
+       u8 messages[256];
+};
+
+struct dst_types {
+       char *device_id;
+       int offset;
+       u8 dst_type;
+       u32 type_flags;
+       u32 dst_feature;
+};
+
+
+
+struct dst_config
+{
+       /* the ASIC i2c address */
+       u8 demod_address;
+};
+
+
+int rdc_reset_state(struct dst_state *state);
+int rdc_8820_reset(struct dst_state *state);
+
+int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode);
+int dst_pio_enable(struct dst_state *state);
+int dst_pio_disable(struct dst_state *state);
+int dst_error_recovery(struct dst_state* state);
+int dst_error_bailout(struct dst_state *state);
+int dst_comm_init(struct dst_state* state);
+
+int write_dst(struct dst_state *state, u8 * data, u8 len);
+int read_dst(struct dst_state *state, u8 * ret, u8 len);
+u8 dst_check_sum(u8 * buf, u32 len);
+struct dst_state* dst_attach(struct dst_state* state, struct dvb_adapter *dvb_adapter);
+int dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter);
+int dst_gpio_outb(struct dst_state* state, u32 mask, u32 enbb, u32 outhigh, int delay);
+
+int dst_command(struct dst_state* state, u8 * data, u8 len);
+
+
+#endif // DST_COMMON_H
diff --git a/drivers/media/dvb/dibusb/dvb-fe-dtt200u.c b/drivers/media/dvb/dibusb/dvb-fe-dtt200u.c
new file mode 100644 (file)
index 0000000..1872aa6
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * dvb-dtt200u-fe.c is a driver which implements the frontend-part of the
+ * Yakumo/Typhoon/Hama USB2.0 boxes. It is hard-wired to the dibusb-driver as
+ * it uses the usb-transfer functions directly (maybe creating a
+ * generic-dvb-usb-lib for all usb-drivers will be reduce some more code.)
+ *
+ * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@desy.de>
+ *
+ * see dvb-dibusb-core.c for copyright details.
+ */
+
+/* guessed protocol description (reverse engineered):
+ * read
+ *  00 - USB type 0x02 for usb2.0, 0x01 for usb1.1
+ *  81 - <TS_LOCK> <current frequency divided by 250000>
+ *  82 - crash - do not touch
+ *  83 - crash - do not touch
+ *  84 - remote control
+ *  85 - crash - do not touch (OK, stop testing here)
+ *  88 - locking 2 bytes (0x80 0x40 == no signal, 0x89 0x20 == nice signal)
+ *  89 - noise-to-signal
+ *     8a - unkown 1 byte - signal_strength
+ *  8c - ber ???
+ *  8d - ber
+ *  8e - unc
+ *
+ * write
+ *  02 - bandwidth
+ *  03 - frequency (divided by 250000)
+ *  04 - pid table (index pid(7:0) pid(12:8))
+ *  05 - reset the pid table
+ *  08 - demod transfer enabled or not (FX2 transfer is enabled by default)
+ */
+
+#include "dvb-dibusb.h"
+#include "dvb_frontend.h"
+
+struct dtt200u_fe_state {
+       struct usb_dibusb *dib;
+
+       struct dvb_frontend_parameters fep;
+       struct dvb_frontend frontend;
+};
+
+#define moan(which,what) info("unexpected value in '%s' for cmd '%02x' - please report to linux-dvb@linuxtv.org",which,what)
+
+static int dtt200u_fe_read_status(struct dvb_frontend* fe, fe_status_t *stat)
+{
+       struct dtt200u_fe_state *state = fe->demodulator_priv;
+       u8 bw[1] = { 0x81 };
+       u8 br[3] = { 0 };
+//     u8 bdeb[5] = { 0 };
+
+       dibusb_readwrite_usb(state->dib,bw,1,br,3);
+       switch (br[0]) {
+               case 0x01:
+                       *stat = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+                       break;
+               case 0x00:
+                       *stat = 0;
+                       break;
+               default:
+                       moan("br[0]",0x81);
+                       break;
+       }
+
+//     bw[0] = 0x88;
+//     dibusb_readwrite_usb(state->dib,bw,1,bdeb,5);
+
+//     deb_info("%02x: %02x %02x %02x %02x %02x\n",bw[0],bdeb[0],bdeb[1],bdeb[2],bdeb[3],bdeb[4]);
+
+       return 0;
+}
+static int dtt200u_fe_read_ber(struct dvb_frontend* fe, u32 *ber)
+{
+       struct dtt200u_fe_state *state = fe->demodulator_priv;
+       u8 bw[1] = { 0x8d };
+       *ber = 0;
+       dibusb_readwrite_usb(state->dib,bw,1,(u8*) ber, 3);
+       return 0;
+}
+
+static int dtt200u_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
+{
+       struct dtt200u_fe_state *state = fe->demodulator_priv;
+       u8 bw[1] = { 0x8c };
+       *unc = 0;
+       dibusb_readwrite_usb(state->dib,bw,1,(u8*) unc, 3);
+       return 0;
+}
+
+static int dtt200u_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
+{
+       struct dtt200u_fe_state *state = fe->demodulator_priv;
+       u8 bw[1] = { 0x8a };
+       u8 b;
+       dibusb_readwrite_usb(state->dib,bw,1,&b, 1);
+       *strength = (b << 8) | b;
+       return 0;
+}
+
+static int dtt200u_fe_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+       struct dtt200u_fe_state *state = fe->demodulator_priv;
+       u8 bw[1] = { 0x89 };
+       u8 br[1] = { 0 };
+       dibusb_readwrite_usb(state->dib,bw,1,br,1);
+       *snr = ((0xff - br[0]) << 8) | (0xff - br[0]);
+       return 0;
+}
+
+static int dtt200u_fe_init(struct dvb_frontend* fe)
+{
+       struct dtt200u_fe_state *state = fe->demodulator_priv;
+       u8 b[] = { 0x01 };
+       return dibusb_write_usb(state->dib,b,1);
+}
+
+static int dtt200u_fe_sleep(struct dvb_frontend* fe)
+{
+       return dtt200u_fe_init(fe);
+}
+
+static int dtt200u_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+       tune->min_delay_ms = 1500;
+       tune->step_size = 166667;
+       tune->max_drift = 166667 * 2;
+       return 0;
+}
+
+static int dtt200u_fe_set_frontend(struct dvb_frontend* fe,
+                                 struct dvb_frontend_parameters *fep)
+{
+       struct dtt200u_fe_state *state = fe->demodulator_priv;
+       u16 freq = fep->frequency / 250000;
+       u8 bw,bwbuf[2] = { 0x03, 0 }, freqbuf[3] = { 0x02, 0, 0 };
+
+       switch (fep->u.ofdm.bandwidth) {
+               case BANDWIDTH_8_MHZ: bw = 8; break;
+               case BANDWIDTH_7_MHZ: bw = 7; break;
+               case BANDWIDTH_6_MHZ: bw = 6; break;
+               case BANDWIDTH_AUTO: return -EOPNOTSUPP;
+               default:
+                       return -EINVAL;
+       }
+       deb_info("set_frontend\n");
+
+       bwbuf[1] = bw;
+       dibusb_write_usb(state->dib,bwbuf,2);
+
+       freqbuf[1] = freq & 0xff;
+       freqbuf[2] = (freq >> 8) & 0xff;
+       dibusb_write_usb(state->dib,freqbuf,3);
+
+       memcpy(&state->fep,fep,sizeof(struct dvb_frontend_parameters));
+
+       return 0;
+}
+
+static int dtt200u_fe_get_frontend(struct dvb_frontend* fe,
+                                 struct dvb_frontend_parameters *fep)
+{
+       struct dtt200u_fe_state *state = fe->demodulator_priv;
+       memcpy(fep,&state->fep,sizeof(struct dvb_frontend_parameters));
+       return 0;
+}
+
+static void dtt200u_fe_release(struct dvb_frontend* fe)
+{
+       struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv;
+       kfree(state);
+}
+
+static int dtt200u_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff)
+{
+       struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv;
+       u8 b_pid[4];
+       pid = onoff ? pid : 0;
+
+       b_pid[0] = 0x04;
+       b_pid[1] = index;
+       b_pid[2] = pid & 0xff;
+       b_pid[3] = (pid >> 8) & 0xff;
+
+       dibusb_write_usb(state->dib,b_pid,4);
+       return 0;
+}
+
+static int dtt200u_fifo_control(struct dvb_frontend *fe, int onoff)
+{
+       struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv;
+       u8 b_streaming[2] = { 0x08, onoff };
+       u8 b_rst_pid[1] = { 0x05 };
+
+       dibusb_write_usb(state->dib,b_streaming,2);
+
+       if (!onoff)
+               dibusb_write_usb(state->dib,b_rst_pid,1);
+       return 0;
+}
+
+static struct dvb_frontend_ops dtt200u_fe_ops;
+
+struct dvb_frontend* dtt200u_fe_attach(struct usb_dibusb *dib, struct dib_fe_xfer_ops *xfer_ops)
+{
+       struct dtt200u_fe_state* state = NULL;
+
+       /* allocate memory for the internal state */
+       state = (struct dtt200u_fe_state*) kmalloc(sizeof(struct dtt200u_fe_state), GFP_KERNEL);
+       if (state == NULL)
+               goto error;
+       memset(state,0,sizeof(struct dtt200u_fe_state));
+
+       deb_info("attaching frontend dtt200u\n");
+
+       state->dib = dib;
+
+       state->frontend.ops = &dtt200u_fe_ops;
+       state->frontend.demodulator_priv = state;
+
+       xfer_ops->fifo_ctrl = dtt200u_fifo_control;
+       xfer_ops->pid_ctrl = dtt200u_pid_control;
+
+       goto success;
+error:
+       return NULL;
+success:
+       return &state->frontend;
+}
+
+static struct dvb_frontend_ops dtt200u_fe_ops = {
+       .info = {
+               .name                   = "DTT200U (Yakumo/Typhoon/Hama) DVB-T",
+               .type                   = FE_OFDM,
+               .frequency_min          = 44250000,
+               .frequency_max          = 867250000,
+               .frequency_stepsize     = 250000,
+               .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_RECOVER |
+                               FE_CAN_HIERARCHY_AUTO,
+       },
+
+       .release = dtt200u_fe_release,
+
+       .init = dtt200u_fe_init,
+       .sleep = dtt200u_fe_sleep,
+
+       .set_frontend = dtt200u_fe_set_frontend,
+       .get_frontend = dtt200u_fe_get_frontend,
+       .get_tune_settings = dtt200u_fe_get_tune_settings,
+
+       .read_status = dtt200u_fe_read_status,
+       .read_ber = dtt200u_fe_read_ber,
+       .read_signal_strength = dtt200u_fe_read_signal_strength,
+       .read_snr = dtt200u_fe_read_snr,
+       .read_ucblocks = dtt200u_fe_read_unc_blocks,
+};
diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
new file mode 100644 (file)
index 0000000..2a3c2ce
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * $Id: dvb-pll.c,v 1.7 2005/02/10 11:52:02 kraxel Exp $
+ *
+ * descriptions + helper functions for simple dvb plls.
+ *
+ * (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/dvb/frontend.h>
+#include <asm/types.h>
+
+#include "dvb-pll.h"
+
+/* ----------------------------------------------------------- */
+/* descriptions                                                */
+
+struct dvb_pll_desc dvb_pll_thomson_dtt7579 = {
+       .name  = "Thomson dtt7579",
+       .min   = 177000000,
+       .max   = 858000000,
+       .count = 5,
+       .entries = {
+               {          0, 36166667, 166666, 0xb4, 0x03 }, /* go sleep */
+               {  443250000, 36166667, 166666, 0xb4, 0x02 },
+               {  542000000, 36166667, 166666, 0xb4, 0x08 },
+               {  771000000, 36166667, 166666, 0xbc, 0x08 },
+               {  999999999, 36166667, 166666, 0xf4, 0x08 },
+       },
+};
+EXPORT_SYMBOL(dvb_pll_thomson_dtt7579);
+
+struct dvb_pll_desc dvb_pll_thomson_dtt7610 = {
+       .name  = "Thomson dtt7610",
+       .min   =  44000000,
+       .max   = 958000000,
+       .count = 3,
+       .entries = {
+               { 157250000, 44000000, 62500, 0x8e, 0x39 },
+               { 454000000, 44000000, 62500, 0x8e, 0x3a },
+               { 999999999, 44000000, 62500, 0x8e, 0x3c },
+       },
+};
+EXPORT_SYMBOL(dvb_pll_thomson_dtt7610);
+
+static void thomson_dtt759x_bw(u8 *buf, int bandwidth)
+{
+       if (BANDWIDTH_7_MHZ == bandwidth)
+               buf[3] |= 0x10;
+}
+
+struct dvb_pll_desc dvb_pll_thomson_dtt759x = {
+       .name  = "Thomson dtt759x",
+       .min   = 177000000,
+       .max   = 896000000,
+       .setbw = thomson_dtt759x_bw,
+       .count = 6,
+       .entries = {
+               {          0, 36166667, 166666, 0x84, 0x03 },
+               {  264000000, 36166667, 166666, 0xb4, 0x02 },
+               {  470000000, 36166667, 166666, 0xbc, 0x02 },
+               {  735000000, 36166667, 166666, 0xbc, 0x08 },
+               {  835000000, 36166667, 166666, 0xf4, 0x08 },
+               {  999999999, 36166667, 166666, 0xfc, 0x08 },
+       },
+};
+EXPORT_SYMBOL(dvb_pll_thomson_dtt759x);
+
+struct dvb_pll_desc dvb_pll_lg_z201 = {
+       .name  = "LG z201",
+       .min   = 174000000,
+       .max   = 862000000,
+       .count = 5,
+       .entries = {
+               {          0, 36166667, 166666, 0xbc, 0x03 },
+               {  443250000, 36166667, 166666, 0xbc, 0x01 },
+               {  542000000, 36166667, 166666, 0xbc, 0x02 },
+               {  830000000, 36166667, 166666, 0xf4, 0x02 },
+               {  999999999, 36166667, 166666, 0xfc, 0x02 },
+       },
+};
+EXPORT_SYMBOL(dvb_pll_lg_z201);
+
+struct dvb_pll_desc dvb_pll_unknown_1 = {
+       .name  = "unknown 1", /* used by dntv live dvb-t */
+       .min   = 174000000,
+       .max   = 862000000,
+       .count = 9,
+       .entries = {
+               {  150000000, 36166667, 166666, 0xb4, 0x01 },
+               {  173000000, 36166667, 166666, 0xbc, 0x01 },
+               {  250000000, 36166667, 166666, 0xb4, 0x02 },
+               {  400000000, 36166667, 166666, 0xbc, 0x02 },
+               {  420000000, 36166667, 166666, 0xf4, 0x02 },
+               {  470000000, 36166667, 166666, 0xfc, 0x02 },
+               {  600000000, 36166667, 166666, 0xbc, 0x08 },
+               {  730000000, 36166667, 166666, 0xf4, 0x08 },
+               {  999999999, 36166667, 166666, 0xfc, 0x08 },
+       },
+};
+EXPORT_SYMBOL(dvb_pll_unknown_1);
+
+/* ----------------------------------------------------------- */
+/* code                                                        */
+
+static int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable verbose debug messages");
+
+int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
+                     u32 freq, int bandwidth)
+{
+       u32 div;
+       int i;
+
+       if (freq != 0 && (freq < desc->min || freq > desc->max))
+           return -EINVAL;
+
+       for (i = 0; i < desc->count; i++) {
+               if (freq > desc->entries[i].limit)
+                       continue;
+               break;
+       }
+       if (debug)
+               printk("pll: %s: freq=%d bw=%d | i=%d/%d\n",
+                      desc->name, freq, bandwidth, i, desc->count);
+       BUG_ON(i == desc->count);
+
+       div = (freq + desc->entries[i].offset) / desc->entries[i].stepsize;
+       buf[0] = div >> 8;
+       buf[1] = div & 0xff;
+       buf[2] = desc->entries[i].cb1;
+       buf[3] = desc->entries[i].cb2;
+
+       if (desc->setbw)
+               desc->setbw(buf, bandwidth);
+
+       if (debug)
+               printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
+                      desc->name, div, buf[0], buf[1], buf[2], buf[3]);
+
+       return 0;
+}
+EXPORT_SYMBOL(dvb_pll_configure);
+
+MODULE_DESCRIPTION("dvb pll library");
+MODULE_AUTHOR("Gerd Knorr");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h
new file mode 100644 (file)
index 0000000..c4c3c56
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * $Id: dvb-pll.h,v 1.2 2005/02/10 11:43:41 kraxel Exp $
+ */
+
+#ifndef __DVB_PLL_H__
+#define __DVB_PLL_H__
+
+struct dvb_pll_desc {
+       char *name;
+       u32  min;
+       u32  max;
+       void (*setbw)(u8 *buf, int bandwidth);
+       int  count;
+       struct {
+               u32 limit;
+               u32 offset;
+               u32 stepsize;
+               u8  cb1;
+               u8  cb2;
+       } entries[9];
+};
+
+extern struct dvb_pll_desc dvb_pll_thomson_dtt7579;
+extern struct dvb_pll_desc dvb_pll_thomson_dtt759x;
+extern struct dvb_pll_desc dvb_pll_thomson_dtt7610;
+extern struct dvb_pll_desc dvb_pll_lg_z201;
+extern struct dvb_pll_desc dvb_pll_unknown_1;
+
+int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
+                     u32 freq, int bandwidth);
+
+#endif
diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c
new file mode 100644 (file)
index 0000000..cc0a77c
--- /dev/null
@@ -0,0 +1,628 @@
+/*
+ *    Support for OR51132 (pcHDTV HD-3000) - VSB/QAM
+ *
+ *    Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
+ *
+ *    Based on code from Jack Kelliher (kelliher@xmission.com)
+ *                           Copyright (C) 2002 & pcHDTV, 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 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 two external firmware files. Please copy
+ * "dvb-fe-or51132-vsb.fw" and "dvb-fe-or51132-qam.fw" to
+ * /usr/lib/hotplug/firmware/ or /lib/firmware/
+ * (depending on configuration of firmware hotplug).
+ */
+#define OR51132_VSB_FIRMWARE "dvb-fe-or51132-vsb.fw"
+#define OR51132_QAM_FIRMWARE "dvb-fe-or51132-qam.fw"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+
+#include "dvb_frontend.h"
+#include "dvb-pll.h"
+#include "or51132.h"
+
+static int debug;
+#define dprintk(args...) \
+       do { \
+               if (debug) printk(KERN_DEBUG "or51132: " args); \
+       } while (0)
+
+
+struct or51132_state
+{
+       struct i2c_adapter* i2c;
+       struct dvb_frontend_ops ops;
+
+       /* Configuration settings */
+       const struct or51132_config* config;
+
+       struct dvb_frontend frontend;
+
+       /* Demodulator private data */
+       fe_modulation_t current_modulation;
+
+       /* Tuner private data */
+       u32 current_frequency;
+};
+
+static int i2c_writebytes (struct or51132_state* state, u8 reg, u8 *buf, int len)
+{
+       int err;
+       struct i2c_msg msg;
+       msg.addr  = reg;
+       msg.flags = 0;
+       msg.len   = len;
+       msg.buf   = buf;
+
+       if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+               printk(KERN_WARNING "or51132: i2c_writebytes error (addr %02x, err == %i)\n", reg, err);
+               return -EREMOTEIO;
+       }
+
+       return 0;
+}
+
+static u8 i2c_readbytes (struct or51132_state* state, u8 reg, u8* buf, int len)
+{
+       int err;
+       struct i2c_msg msg;
+       msg.addr   = reg;
+       msg.flags = I2C_M_RD;
+       msg.len = len;
+       msg.buf = buf;
+
+       if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+               printk(KERN_WARNING "or51132: i2c_readbytes error (addr %02x, err == %i)\n", reg, err);
+               return -EREMOTEIO;
+       }
+
+       return 0;
+}
+
+static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
+{
+       struct or51132_state* state = fe->demodulator_priv;
+       static u8 run_buf[] = {0x7F,0x01};
+       static u8 get_ver_buf[] = {0x04,0x00,0x30,0x00,0x00};
+       u8 rec_buf[14];
+       u8 cmd_buf[14];
+       u32 firmwareAsize, firmwareBsize;
+       int i,ret;
+
+       dprintk("Firmware is %Zd bytes\n",fw->size);
+
+       /* Get size of firmware A and B */
+       firmwareAsize = le32_to_cpu(*((u32*)fw->data));
+       dprintk("FirmwareA is %i bytes\n",firmwareAsize);
+       firmwareBsize = le32_to_cpu(*((u32*)(fw->data+4)));
+       dprintk("FirmwareB is %i bytes\n",firmwareBsize);
+
+       /* Upload firmware */
+       if ((ret = i2c_writebytes(state,state->config->demod_address,
+                                &fw->data[8],firmwareAsize))) {
+               printk(KERN_WARNING "or51132: load_firmware error 1\n");
+               return ret;
+       }
+       msleep(1); /* 1ms */
+       if ((ret = i2c_writebytes(state,state->config->demod_address,
+                                &fw->data[8+firmwareAsize],firmwareBsize))) {
+               printk(KERN_WARNING "or51132: load_firmware error 2\n");
+               return ret;
+       }
+       msleep(1); /* 1ms */
+
+       if ((ret = i2c_writebytes(state,state->config->demod_address,
+                                run_buf,2))) {
+               printk(KERN_WARNING "or51132: load_firmware error 3\n");
+               return ret;
+       }
+
+       /* Wait at least 5 msec */
+       msleep(20); /* 10ms */
+
+       if ((ret = i2c_writebytes(state,state->config->demod_address,
+                                run_buf,2))) {
+               printk(KERN_WARNING "or51132: load_firmware error 4\n");
+               return ret;
+       }
+
+       /* 50ms for operation to begin */
+       msleep(50);
+
+       /* Read back ucode version to besure we loaded correctly and are really up and running */
+       /* Get uCode version */
+       cmd_buf[0] = 0x10;
+       cmd_buf[1] = 0x10;
+       cmd_buf[2] = 0x00;
+       cmd_buf[3] = 0x00;
+       msleep(20); /* 20ms */
+       if ((ret = i2c_writebytes(state,state->config->demod_address,
+                                cmd_buf,3))) {
+               printk(KERN_WARNING "or51132: load_firmware error a\n");
+               return ret;
+       }
+
+       cmd_buf[0] = 0x04;
+       cmd_buf[1] = 0x17;
+       cmd_buf[2] = 0x00;
+       cmd_buf[3] = 0x00;
+       msleep(20); /* 20ms */
+       if ((ret = i2c_writebytes(state,state->config->demod_address,
+                                cmd_buf,2))) {
+               printk(KERN_WARNING "or51132: load_firmware error b\n");
+               return ret;
+       }
+
+       cmd_buf[0] = 0x00;
+       cmd_buf[1] = 0x00;
+       cmd_buf[2] = 0x00;
+       cmd_buf[3] = 0x00;
+       msleep(20); /* 20ms */
+       if ((ret = i2c_writebytes(state,state->config->demod_address,
+                                cmd_buf,2))) {
+               printk(KERN_WARNING "or51132: load_firmware error c\n");
+               return ret;
+       }
+
+       for(i=0;i<4;i++) {
+               msleep(20); /* 20ms */
+               get_ver_buf[4] = i+1;
+               if ((ret = i2c_readbytes(state,state->config->demod_address,
+                                       &rec_buf[i*2],2))) {
+                       printk(KERN_WARNING
+                              "or51132: load_firmware error d - %d\n",i);
+                       return ret;
+               }
+       }
+
+       printk(KERN_WARNING
+              "or51132: Version: %02X%02X%02X%02X-%02X%02X%02X%02X (%02X%01X-%01X-%02X%01X-%01X)\n",
+              rec_buf[1],rec_buf[0],rec_buf[3],rec_buf[2],
+              rec_buf[5],rec_buf[4],rec_buf[7],rec_buf[6],
+              rec_buf[3],rec_buf[2]>>4,rec_buf[2]&0x0f,
+              rec_buf[5],rec_buf[4]>>4,rec_buf[4]&0x0f);
+
+       cmd_buf[0] = 0x10;
+       cmd_buf[1] = 0x00;
+       cmd_buf[2] = 0x00;
+       cmd_buf[3] = 0x00;
+       msleep(20); /* 20ms */
+       if ((ret = i2c_writebytes(state,state->config->demod_address,
+                                cmd_buf,3))) {
+               printk(KERN_WARNING "or51132: load_firmware error e\n");
+               return ret;
+       }
+       return 0;
+};
+
+static int or51132_init(struct dvb_frontend* fe)
+{
+       return 0;
+}
+
+static int or51132_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+       *ber = 0;
+       return 0;
+}
+
+static int or51132_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+       *ucblocks = 0;
+       return 0;
+}
+
+static int or51132_sleep(struct dvb_frontend* fe)
+{
+       return 0;
+}
+
+static int or51132_setmode(struct dvb_frontend* fe)
+{
+       struct or51132_state* state = fe->demodulator_priv;
+       unsigned char cmd_buf[4];
+
+       dprintk("setmode %d\n",(int)state->current_modulation);
+       /* set operation mode in Receiver 1 register; */
+       cmd_buf[0] = 0x04;
+       cmd_buf[1] = 0x01;
+       switch (state->current_modulation) {
+       case QAM_256:
+       case QAM_64:
+       case QAM_AUTO:
+               /* Auto-deinterleave; MPEG ser, MPEG2tr, phase noise-high*/
+               cmd_buf[2] = 0x5F;
+               break;
+       case VSB_8:
+               /* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high*/
+               cmd_buf[2] = 0x50;
+               break;
+       default:
+               printk("setmode:Modulation set to unsupported value\n");
+       };
+       cmd_buf[3] = 0x00;
+       if (i2c_writebytes(state,state->config->demod_address,
+                          cmd_buf,3)) {
+               printk(KERN_WARNING "or51132: set_mode error 1\n");
+               return -1;
+       }
+       dprintk("or51132: set #1 to %02x\n", cmd_buf[2]);
+
+       /* Set operation mode in Receiver 6 register */
+       cmd_buf[0] = 0x1C;
+       switch (state->current_modulation) {
+       case QAM_AUTO:
+               /* REC MODE Normal Carrier Lock */
+               cmd_buf[1] = 0x00;
+               /* Channel MODE Auto QAM64/256 */
+               cmd_buf[2] = 0x4f;
+               break;
+       case QAM_256:
+               /* REC MODE Normal Carrier Lock */
+               cmd_buf[1] = 0x00;
+               /* Channel MODE QAM256 */
+               cmd_buf[2] = 0x45;
+               break;
+       case QAM_64:
+               /* REC MODE Normal Carrier Lock */
+               cmd_buf[1] = 0x00;
+               /* Channel MODE QAM64 */
+               cmd_buf[2] = 0x43;
+               break;
+       case VSB_8:
+                /* REC MODE inv IF spectrum, Normal */
+               cmd_buf[1] = 0x03;
+               /* Channel MODE ATSC/VSB8 */
+               cmd_buf[2] = 0x06;
+               break;
+       default:
+               printk("setmode: Modulation set to unsupported value\n");
+       };
+       cmd_buf[3] = 0x00;
+       msleep(20); /* 20ms */
+       if (i2c_writebytes(state,state->config->demod_address,
+                          cmd_buf,3)) {
+               printk(KERN_WARNING "or51132: set_mode error 2\n");
+               return -1;
+       }
+       dprintk("or51132: set #6 to 0x%02x%02x\n", cmd_buf[1], cmd_buf[2]);
+
+       return 0;
+}
+
+static int or51132_set_parameters(struct dvb_frontend* fe,
+                                 struct dvb_frontend_parameters *param)
+{
+       int ret;
+       u8 buf[4];
+       struct or51132_state* state = fe->demodulator_priv;
+       const struct firmware *fw;
+
+       /* Change only if we are actually changing the modulation */
+       if (state->current_modulation != param->u.vsb.modulation) {
+               switch(param->u.vsb.modulation) {
+               case VSB_8:
+                       dprintk("set_parameters VSB MODE\n");
+                       printk("or51132: Waiting for firmware upload(%s)...\n",
+                              OR51132_VSB_FIRMWARE);
+                       ret = request_firmware(&fw, OR51132_VSB_FIRMWARE,
+                                              &state->i2c->dev);
+                       if (ret){
+                               printk(KERN_WARNING "or51132: No firmware up"
+                                      "loaded(timeout or file not found?)\n");
+                               return ret;
+                       }
+                       /* Set non-punctured clock for VSB */
+                       state->config->set_ts_params(fe, 0);
+                       break;
+               case QAM_AUTO:
+               case QAM_64:
+               case QAM_256:
+                       dprintk("set_parameters QAM MODE\n");
+                       printk("or51132: Waiting for firmware upload(%s)...\n",
+                              OR51132_QAM_FIRMWARE);
+                       ret = request_firmware(&fw, OR51132_QAM_FIRMWARE,
+                                              &state->i2c->dev);
+                       if (ret){
+                               printk(KERN_WARNING "or51132: No firmware up"
+                                      "loaded(timeout or file not found?)\n");
+                               return ret;
+                       }
+                       /* Set punctured clock for QAM */
+                       state->config->set_ts_params(fe, 1);
+                       break;
+               default:
+                       printk("or51132:Modulation type(%d) UNSUPPORTED\n",
+                              param->u.vsb.modulation);
+                       return -1;
+               };
+               ret = or51132_load_firmware(fe, fw);
+               release_firmware(fw);
+               if (ret) {
+                       printk(KERN_WARNING "or51132: Writing firmware to "
+                              "device failed!\n");
+                       return ret;
+               }
+               printk("or51132: Firmware upload complete.\n");
+
+               state->current_modulation = param->u.vsb.modulation;
+               or51132_setmode(fe);
+       }
+
+       /* Change only if we are actually changing the channel */
+       if (state->current_frequency != param->frequency) {
+               dvb_pll_configure(state->config->pll_desc, buf,
+                                 param->frequency, 0);
+               dprintk("set_parameters tuner bytes: 0x%02x 0x%02x "
+                       "0x%02x 0x%02x\n",buf[0],buf[1],buf[2],buf[3]);
+               if (i2c_writebytes(state, state->config->pll_address ,buf, 4))
+                       printk(KERN_WARNING "or51132: set_parameters error "
+                              "writing to tuner\n");
+
+               /* Set to current mode */
+               or51132_setmode(fe);
+
+               /* Update current frequency */
+               state->current_frequency = param->frequency;
+       }
+       return 0;
+}
+
+static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+       struct or51132_state* state = fe->demodulator_priv;
+       unsigned char rec_buf[2];
+       unsigned char snd_buf[2];
+       *status = 0;
+
+       /* Receiver Status */
+       snd_buf[0]=0x04;
+       snd_buf[1]=0x00;
+       msleep(30); /* 30ms */
+       if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
+               printk(KERN_WARNING "or51132: read_status write error\n");
+               return -1;
+       }
+       msleep(30); /* 30ms */
+       if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
+               printk(KERN_WARNING "or51132: read_status read error\n");
+               return -1;
+       }
+       dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]);
+
+       if (rec_buf[1] & 0x01) { /* Receiver Lock */
+               *status |= FE_HAS_SIGNAL;
+               *status |= FE_HAS_CARRIER;
+               *status |= FE_HAS_VITERBI;
+               *status |= FE_HAS_SYNC;
+               *status |= FE_HAS_LOCK;
+       }
+       return 0;
+}
+
+/* log10-1 table at .5 increments from 1 to 100.5 */
+static unsigned int i100x20log10[] = {
+     0,  352,  602,  795,  954, 1088, 1204, 1306, 1397, 1480,
+  1556, 1625, 1690, 1750, 1806, 1858, 1908, 1955, 2000, 2042,
+  2082, 2121, 2158, 2193, 2227, 2260, 2292, 2322, 2352, 2380,
+  2408, 2434, 2460, 2486, 2510, 2534, 2557, 2580, 2602, 2623,
+  2644, 2664, 2684, 2704, 2723, 2742, 2760, 2778, 2795, 2813,
+  2829, 2846, 2862, 2878, 2894, 2909, 2924, 2939, 2954, 2968,
+  2982, 2996, 3010, 3023, 3037, 3050, 3062, 3075, 3088, 3100,
+  3112, 3124, 3136, 3148, 3159, 3170, 3182, 3193, 3204, 3214,
+  3225, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316,
+  3325, 3334, 3344, 3353, 3362, 3371, 3380, 3389, 3397, 3406,
+  3415, 3423, 3432, 3440, 3448, 3456, 3464, 3472, 3480, 3488,
+  3496, 3504, 3511, 3519, 3526, 3534, 3541, 3549, 3556, 3563,
+  3570, 3577, 3584, 3591, 3598, 3605, 3612, 3619, 3625, 3632,
+  3639, 3645, 3652, 3658, 3665, 3671, 3677, 3683, 3690, 3696,
+  3702, 3708, 3714, 3720, 3726, 3732, 3738, 3744, 3750, 3755,
+  3761, 3767, 3772, 3778, 3784, 3789, 3795, 3800, 3806, 3811,
+  3816, 3822, 3827, 3832, 3838, 3843, 3848, 3853, 3858, 3863,
+  3868, 3874, 3879, 3884, 3888, 3893, 3898, 3903, 3908, 3913,
+  3918, 3922, 3927, 3932, 3936, 3941, 3946, 3950, 3955, 3960,
+  3964, 3969, 3973, 3978, 3982, 3986, 3991, 3995, 4000, 4004,
+};
+
+static unsigned int denom[] = {1,1,100,1000,10000,100000,1000000,10000000,100000000};
+
+static unsigned int i20Log10(unsigned short val)
+{
+       unsigned int rntval = 100;
+       unsigned int tmp = val;
+       unsigned int exp = 1;
+
+       while(tmp > 100) {tmp /= 100; exp++;}
+
+       val = (2 * val)/denom[exp];
+       if (exp > 1) rntval = 2000*exp;
+
+       rntval += i100x20log10[val];
+       return rntval;
+}
+
+static int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+       struct or51132_state* state = fe->demodulator_priv;
+       unsigned char rec_buf[2];
+       unsigned char snd_buf[2];
+       u8 rcvr_stat;
+       u16 snr_equ;
+       int usK;
+
+       snd_buf[0]=0x04;
+       snd_buf[1]=0x02; /* SNR after Equalizer */
+       msleep(30); /* 30ms */
+       if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
+               printk(KERN_WARNING "or51132: read_status write error\n");
+               return -1;
+       }
+       msleep(30); /* 30ms */
+       if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
+               printk(KERN_WARNING "or51132: read_status read error\n");
+               return -1;
+       }
+       snr_equ = rec_buf[0] | (rec_buf[1] << 8);
+       dprintk("read_signal_strength snr_equ %x %x (%i)\n",rec_buf[0],rec_buf[1],snr_equ);
+
+       /* Receiver Status */
+       snd_buf[0]=0x04;
+       snd_buf[1]=0x00;
+       msleep(30); /* 30ms */
+       if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
+               printk(KERN_WARNING "or51132: read_signal_strength read_status write error\n");
+               return -1;
+       }
+       msleep(30); /* 30ms */
+       if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
+               printk(KERN_WARNING "or51132: read_signal_strength read_status read error\n");
+               return -1;
+       }
+       dprintk("read_signal_strength read_status %x %x\n",rec_buf[0],rec_buf[1]);
+       rcvr_stat = rec_buf[1];
+       usK = (rcvr_stat & 0x10) ? 3 : 0;
+
+        /* The value reported back from the frontend will be FFFF=100% 0000=0% */
+       *strength = (((8952 - i20Log10(snr_equ) - usK*100)/3+5)*65535)/1000;
+       dprintk("read_signal_strength %i\n",*strength);
+
+       return 0;
+}
+
+static int or51132_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+       struct or51132_state* state = fe->demodulator_priv;
+       unsigned char rec_buf[2];
+       unsigned char snd_buf[2];
+       u16 snr_equ;
+
+       snd_buf[0]=0x04;
+       snd_buf[1]=0x02; /* SNR after Equalizer */
+       msleep(30); /* 30ms */
+       if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
+               printk(KERN_WARNING "or51132: read_snr write error\n");
+               return -1;
+       }
+       msleep(30); /* 30ms */
+       if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
+               printk(KERN_WARNING "or51132: read_snr dvr read error\n");
+               return -1;
+       }
+       snr_equ = rec_buf[0] | (rec_buf[1] << 8);
+       dprintk("read_snr snr_equ %x %x (%i)\n",rec_buf[0],rec_buf[1],snr_equ);
+
+       *snr = 0xFFFF - snr_equ;
+       dprintk("read_snr %i\n",*snr);
+
+       return 0;
+}
+
+static int or51132_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings)
+{
+       fe_tune_settings->min_delay_ms = 500;
+       fe_tune_settings->step_size = 0;
+       fe_tune_settings->max_drift = 0;
+
+       return 0;
+}
+
+static void or51132_release(struct dvb_frontend* fe)
+{
+       struct or51132_state* state = fe->demodulator_priv;
+       kfree(state);
+}
+
+static struct dvb_frontend_ops or51132_ops;
+
+struct dvb_frontend* or51132_attach(const struct or51132_config* config,
+                                   struct i2c_adapter* i2c)
+{
+       struct or51132_state* state = NULL;
+
+       /* Allocate memory for the internal state */
+       state = kmalloc(sizeof(struct or51132_state), GFP_KERNEL);
+       if (state == NULL)
+               goto error;
+
+       /* Setup the state */
+       state->config = config;
+       state->i2c = i2c;
+       memcpy(&state->ops, &or51132_ops, sizeof(struct dvb_frontend_ops));
+       state->current_frequency = -1;
+       state->current_modulation = -1;
+
+       /* 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 or51132_ops = {
+
+       .info = {
+               .name                   = "Oren OR51132 VSB/QAM Frontend",
+               .type                   = FE_ATSC,
+               .frequency_min          = 44000000,
+               .frequency_max          = 958000000,
+               .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_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO |
+                       FE_CAN_8VSB
+       },
+
+       .release = or51132_release,
+
+       .init = or51132_init,
+       .sleep = or51132_sleep,
+
+       .set_frontend = or51132_set_parameters,
+       .get_tune_settings = or51132_get_tune_settings,
+
+       .read_status = or51132_read_status,
+       .read_ber = or51132_read_ber,
+       .read_signal_strength = or51132_read_signal_strength,
+       .read_snr = or51132_read_snr,
+       .read_ucblocks = or51132_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("OR51132 ATSC [pcHDTV HD-3000] (8VSB & ITU J83 AnnexB FEC QAM64/256) Demodulator Driver");
+MODULE_AUTHOR("Kirk Lapray");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(or51132_attach);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/or51132.h b/drivers/media/dvb/frontends/or51132.h
new file mode 100644 (file)
index 0000000..622cdd1
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *    Support for OR51132 (pcHDTV HD-3000) - VSB/QAM
+ *
+ *    Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.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 OR51132_H
+#define OR51132_H
+
+#include <linux/firmware.h>
+#include <linux/dvb/frontend.h>
+
+struct or51132_config
+{
+       /* The demodulator's i2c address */
+       u8 demod_address;
+       u8 pll_address;
+       struct dvb_pll_desc *pll_desc;
+
+       /* Need to set device param for start_dma */
+       int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
+};
+
+extern struct dvb_frontend* or51132_attach(const struct or51132_config* config,
+                                          struct i2c_adapter* i2c);
+
+#endif // OR51132_H
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c
new file mode 100644 (file)
index 0000000..ad56a99
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ *    Support for OR51211 (pcHDTV HD-2000) - VSB
+ *
+ *    Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
+ *
+ *    Based on code from Jack Kelliher (kelliher@xmission.com)
+ *                           Copyright (C) 2002 & pcHDTV, 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 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 or51211" to
+ * download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+ */
+#define OR51211_DEFAULT_FIRMWARE "dvb-fe-or51211.fw"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <asm/byteorder.h>
+
+#include "dvb_frontend.h"
+#include "or51211.h"
+
+static int debug;
+#define dprintk(args...) \
+       do { \
+               if (debug) printk(KERN_DEBUG "or51211: " args); \
+       } while (0)
+
+static u8 run_buf[] = {0x7f,0x01};
+static u8 cmd_buf[] = {0x04,0x01,0x50,0x80,0x06}; // ATSC
+
+struct or51211_state {
+
+       struct i2c_adapter* i2c;
+       struct dvb_frontend_ops ops;
+
+       /* Configuration settings */
+       const struct or51211_config* config;
+
+       struct dvb_frontend frontend;
+       struct bt878* bt;
+
+       /* Demodulator private data */
+       u8 initialized:1;
+
+       /* Tuner private data */
+       u32 current_frequency;
+};
+
+static int i2c_writebytes (struct or51211_state* state, u8 reg, u8 *buf,
+                          int len)
+{
+       int err;
+       struct i2c_msg msg;
+       msg.addr        = reg;
+       msg.flags       = 0;
+       msg.len         = len;
+       msg.buf         = buf;
+
+       if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+               printk(KERN_WARNING "or51211: i2c_writebytes error "
+                      "(addr %02x, err == %i)\n", reg, err);
+               return -EREMOTEIO;
+       }
+
+       return 0;
+}
+
+static u8 i2c_readbytes (struct or51211_state* state, u8 reg, u8* buf, int len)
+{
+       int err;
+       struct i2c_msg msg;
+       msg.addr        = reg;
+       msg.flags       = I2C_M_RD;
+       msg.len         = len;
+       msg.buf         = buf;
+
+       if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+               printk(KERN_WARNING "or51211: i2c_readbytes error "
+                      "(addr %02x, err == %i)\n", reg, err);
+               return -EREMOTEIO;
+       }
+
+       return 0;
+}
+
+static int or51211_load_firmware (struct dvb_frontend* fe,
+                                 const struct firmware *fw)
+{
+       struct or51211_state* state = fe->demodulator_priv;
+       u8 tudata[585];
+       int i;
+
+       dprintk("Firmware is %d bytes\n",fw->size);
+
+       /* Get eprom data */
+       tudata[0] = 17;
+       if (i2c_writebytes(state,0x50,tudata,1)) {
+               printk(KERN_WARNING "or51211:load_firmware error eprom addr\n");
+               return -1;
+       }
+       if (i2c_readbytes(state,0x50,&tudata[145],192)) {
+               printk(KERN_WARNING "or51211: load_firmware error eprom\n");
+               return -1;
+       }
+
+       /* Create firmware buffer */
+       for (i = 0; i < 145; i++)
+               tudata[i] = fw->data[i];
+
+       for (i = 0; i < 248; i++)
+               tudata[i+337] = fw->data[145+i];
+
+       state->config->reset(fe);
+
+       if (i2c_writebytes(state,state->config->demod_address,tudata,585)) {
+               printk(KERN_WARNING "or51211: load_firmware error 1\n");
+               return -1;
+       }
+       msleep(1);
+
+       if (i2c_writebytes(state,state->config->demod_address,
+                          &fw->data[393],8125)) {
+               printk(KERN_WARNING "or51211: load_firmware error 2\n");
+               return -1;
+       }
+       msleep(1);
+
+       if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+               printk(KERN_WARNING "or51211: load_firmware error 3\n");
+               return -1;
+       }
+
+       /* Wait at least 5 msec */
+       msleep(10);
+       if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+               printk(KERN_WARNING "or51211: load_firmware error 4\n");
+               return -1;
+       }
+       msleep(10);
+
+       printk("or51211: Done.\n");
+       return 0;
+};
+
+static int or51211_setmode(struct dvb_frontend* fe, int mode)
+{
+       struct or51211_state* state = fe->demodulator_priv;
+       u8 rec_buf[14];
+
+       state->config->setmode(fe, mode);
+
+       if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+               printk(KERN_WARNING "or51211: setmode error 1\n");
+               return -1;
+       }
+
+       /* Wait at least 5 msec */
+       msleep(10);
+       if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+               printk(KERN_WARNING "or51211: setmode error 2\n");
+               return -1;
+       }
+
+       msleep(10);
+
+       /* Set operation mode in Receiver 1 register;
+        * type 1:
+        * data 0x50h  Automatic sets receiver channel conditions
+        *             Automatic NTSC rejection filter
+        *             Enable  MPEG serial data output
+        *             MPEG2tr
+        *             High tuner phase noise
+        *             normal +/-150kHz Carrier acquisition range
+        */
+       if (i2c_writebytes(state,state->config->demod_address,cmd_buf,3)) {
+               printk(KERN_WARNING "or51211: setmode error 3\n");
+               return -1;
+       }
+
+       rec_buf[0] = 0x04;
+       rec_buf[1] = 0x00;
+       rec_buf[2] = 0x03;
+       rec_buf[3] = 0x00;
+       msleep(20);
+       if (i2c_writebytes(state,state->config->demod_address,rec_buf,3)) {
+               printk(KERN_WARNING "or51211: setmode error 5\n");
+       }
+       msleep(3);
+       if (i2c_readbytes(state,state->config->demod_address,&rec_buf[10],2)) {
+               printk(KERN_WARNING "or51211: setmode error 6");
+               return -1;
+       }
+       dprintk("setmode rec status %02x %02x\n",rec_buf[10],rec_buf[11]);
+
+       return 0;
+}
+
+static int or51211_set_parameters(struct dvb_frontend* fe,
+                                 struct dvb_frontend_parameters *param)
+{
+       struct or51211_state* state = fe->demodulator_priv;
+       u32 freq = 0;
+       u16 tunerfreq = 0;
+       u8 buf[4];
+
+       /* Change only if we are actually changing the channel */
+       if (state->current_frequency != param->frequency) {
+               freq = 44000 + (param->frequency/1000);
+               tunerfreq = freq * 16/1000;
+
+               dprintk("set_parameters frequency = %d (tunerfreq = %d)\n",
+                       param->frequency,tunerfreq);
+
+               buf[0] = (tunerfreq >> 8) & 0x7F;
+               buf[1] = (tunerfreq & 0xFF);
+               buf[2] = 0x8E;
+
+               if (param->frequency < 157250000) {
+                       buf[3] = 0xA0;
+                       dprintk("set_parameters VHF low range\n");
+               } else if (param->frequency < 454000000) {
+                       buf[3] = 0x90;
+                       dprintk("set_parameters VHF high range\n");
+               } else {
+                       buf[3] = 0x30;
+                       dprintk("set_parameters UHF range\n");
+               }
+               dprintk("set_parameters tuner bytes: 0x%02x 0x%02x "
+                       "0x%02x 0x%02x\n",buf[0],buf[1],buf[2],buf[3]);
+
+               if (i2c_writebytes(state,0xC2>>1,buf,4))
+                       printk(KERN_WARNING "or51211:set_parameters error "
+                              "writing to tuner\n");
+
+               /* Set to ATSC mode */
+               or51211_setmode(fe,0);
+
+               /* Update current frequency */
+               state->current_frequency = param->frequency;
+       }
+       return 0;
+}
+
+static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+       struct or51211_state* state = fe->demodulator_priv;
+       unsigned char rec_buf[2];
+       unsigned char snd_buf[] = {0x04,0x00,0x03,0x00};
+       *status = 0;
+
+       /* Receiver Status */
+       if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
+               printk(KERN_WARNING "or51132: read_status write error\n");
+               return -1;
+       }
+       msleep(3);
+       if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
+               printk(KERN_WARNING "or51132: read_status read error\n");
+               return -1;
+       }
+       dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]);
+
+       if (rec_buf[0] &  0x01) { /* Receiver Lock */
+               *status |= FE_HAS_SIGNAL;
+               *status |= FE_HAS_CARRIER;
+               *status |= FE_HAS_VITERBI;
+               *status |= FE_HAS_SYNC;
+               *status |= FE_HAS_LOCK;
+       }
+       return 0;
+}
+
+/* log10-1 table at .5 increments from 1 to 100.5 */
+static unsigned int i100x20log10[] = {
+               0,  352,  602,  795,  954, 1088, 1204, 1306, 1397, 1480,
+        1556, 1625, 1690, 1750, 1806, 1858, 1908, 1955, 2000, 2042,
+        2082, 2121, 2158, 2193, 2227, 2260, 2292, 2322, 2352, 2380,
+        2408, 2434, 2460, 2486, 2510, 2534, 2557, 2580, 2602, 2623,
+        2644, 2664, 2684, 2704, 2723, 2742, 2760, 2778, 2795, 2813,
+        2829, 2846, 2862, 2878, 2894, 2909, 2924, 2939, 2954, 2968,
+        2982, 2996, 3010, 3023, 3037, 3050, 3062, 3075, 3088, 3100,
+        3112, 3124, 3136, 3148, 3159, 3170, 3182, 3193, 3204, 3214,
+        3225, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316,
+        3325, 3334, 3344, 3353, 3362, 3371, 3380, 3389, 3397, 3406,
+        3415, 3423, 3432, 3440, 3448, 3456, 3464, 3472, 3480, 3488,
+        3496, 3504, 3511, 3519, 3526, 3534, 3541, 3549, 3556, 3563,
+        3570, 3577, 3584, 3591, 3598, 3605, 3612, 3619, 3625, 3632,
+        3639, 3645, 3652, 3658, 3665, 3671, 3677, 3683, 3690, 3696,
+        3702, 3708, 3714, 3720, 3726, 3732, 3738, 3744, 3750, 3755,
+        3761, 3767, 3772, 3778, 3784, 3789, 3795, 3800, 3806, 3811,
+        3816, 3822, 3827, 3832, 3838, 3843, 3848, 3853, 3858, 3863,
+        3868, 3874, 3879, 3884, 3888, 3893, 3898, 3903, 3908, 3913,
+        3918, 3922, 3927, 3932, 3936, 3941, 3946, 3950, 3955, 3960,
+        3964, 3969, 3973, 3978, 3982, 3986, 3991, 3995, 4000, 4004,
+};
+
+static unsigned int denom[] = {1,1,100,1000,10000,100000,1000000,10000000,100000000};
+
+static unsigned int i20Log10(unsigned short val)
+{
+       unsigned int rntval = 100;
+       unsigned int tmp = val;
+       unsigned int exp = 1;
+
+       while(tmp > 100) {tmp /= 100; exp++;}
+
+       val = (2 * val)/denom[exp];
+       if (exp > 1) rntval = 2000*exp;
+
+       rntval += i100x20log10[val];
+       return rntval;
+}
+
+static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+       struct or51211_state* state = fe->demodulator_priv;
+       u8 rec_buf[2];
+       u8 snd_buf[4];
+       u8 snr_equ;
+
+       /* SNR after Equalizer */
+       snd_buf[0] = 0x04;
+       snd_buf[1] = 0x00;
+       snd_buf[2] = 0x04;
+       snd_buf[3] = 0x00;
+
+       if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
+               printk(KERN_WARNING "or51211: read_status write error\n");
+               return -1;
+       }
+       msleep(3);
+       if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
+               printk(KERN_WARNING "or51211: read_status read error\n");
+               return -1;
+       }
+       snr_equ = rec_buf[0] & 0xff;
+
+       /* The value reported back from the frontend will be FFFF=100% 0000=0% */
+       *strength = (((5334 - i20Log10(snr_equ))/3+5)*65535)/1000;
+
+       dprintk("read_signal_strength %i\n",*strength);
+
+       return 0;
+}
+
+static int or51211_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+       struct or51211_state* state = fe->demodulator_priv;
+       u8 rec_buf[2];
+       u8 snd_buf[4];
+
+       /* SNR after Equalizer */
+       snd_buf[0] = 0x04;
+       snd_buf[1] = 0x00;
+       snd_buf[2] = 0x04;
+       snd_buf[3] = 0x00;
+
+       if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
+               printk(KERN_WARNING "or51211: read_status write error\n");
+               return -1;
+       }
+       msleep(3);
+       if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
+               printk(KERN_WARNING "or51211: read_status read error\n");
+               return -1;
+       }
+       *snr = rec_buf[0] & 0xff;
+
+       dprintk("read_snr %i\n",*snr);
+
+       return 0;
+}
+
+static int or51211_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+       *ber = -ENOSYS;
+       return 0;
+}
+
+static int or51211_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+       *ucblocks = -ENOSYS;
+       return 0;
+}
+
+static int or51211_sleep(struct dvb_frontend* fe)
+{
+       return 0;
+}
+
+static int or51211_init(struct dvb_frontend* fe)
+{
+       struct or51211_state* state = fe->demodulator_priv;
+       const struct or51211_config* config = state->config;
+       const struct firmware* fw;
+       unsigned char get_ver_buf[] = {0x04,0x00,0x30,0x00,0x00};
+       unsigned char rec_buf[14];
+       int ret,i;
+
+       if (!state->initialized) {
+               /* Request the firmware, this will block until it uploads */
+               printk(KERN_INFO "or51211: Waiting for firmware upload "
+                      "(%s)...\n", OR51211_DEFAULT_FIRMWARE);
+               ret = config->request_firmware(fe, &fw,
+                                              OR51211_DEFAULT_FIRMWARE);
+               printk(KERN_INFO "or51211:Got Hotplug firmware\n");
+               if (ret) {
+                       printk(KERN_WARNING "or51211: No firmware uploaded "
+                              "(timeout or file not found?)\n");
+                       return ret;
+               }
+
+               ret = or51211_load_firmware(fe, fw);
+               if (ret) {
+                       printk(KERN_WARNING "or51211: Writing firmware to "
+                              "device failed!\n");
+                       release_firmware(fw);
+                       return ret;
+               }
+               printk(KERN_INFO "or51211: Firmware upload complete.\n");
+
+               /* Set operation mode in Receiver 1 register;
+                * type 1:
+                * data 0x50h  Automatic sets receiver channel conditions
+                *             Automatic NTSC rejection filter
+                *             Enable  MPEG serial data output
+                *             MPEG2tr
+                *             High tuner phase noise
+                *             normal +/-150kHz Carrier acquisition range
+                */
+               if (i2c_writebytes(state,state->config->demod_address,
+                                  cmd_buf,3)) {
+                       printk(KERN_WARNING "or51211: Load DVR Error 5\n");
+                       return -1;
+               }
+
+               /* Read back ucode version to besure we loaded correctly */
+               /* and are really up and running */
+               rec_buf[0] = 0x04;
+               rec_buf[1] = 0x00;
+               rec_buf[2] = 0x03;
+               rec_buf[3] = 0x00;
+               msleep(30);
+               if (i2c_writebytes(state,state->config->demod_address,
+                                  rec_buf,3)) {
+                       printk(KERN_WARNING "or51211: Load DVR Error A\n");
+                       return -1;
+               }
+               msleep(3);
+               if (i2c_readbytes(state,state->config->demod_address,
+                                 &rec_buf[10],2)) {
+                       printk(KERN_WARNING "or51211: Load DVR Error B\n");
+                       return -1;
+               }
+
+               rec_buf[0] = 0x04;
+               rec_buf[1] = 0x00;
+               rec_buf[2] = 0x01;
+               rec_buf[3] = 0x00;
+               msleep(20);
+               if (i2c_writebytes(state,state->config->demod_address,
+                                  rec_buf,3)) {
+                       printk(KERN_WARNING "or51211: Load DVR Error C\n");
+                       return -1;
+               }
+               msleep(3);
+               if (i2c_readbytes(state,state->config->demod_address,
+                                 &rec_buf[12],2)) {
+                       printk(KERN_WARNING "or51211: Load DVR Error D\n");
+                       return -1;
+               }
+
+               for (i = 0; i < 8; i++)
+                       rec_buf[i]=0xed;
+
+               for (i = 0; i < 5; i++) {
+                       msleep(30);
+                       get_ver_buf[4] = i+1;
+                       if (i2c_writebytes(state,state->config->demod_address,
+                                          get_ver_buf,5)) {
+                               printk(KERN_WARNING "or51211:Load DVR Error 6"
+                                      " - %d\n",i);
+                               return -1;
+                       }
+                       msleep(3);
+
+                       if (i2c_readbytes(state,state->config->demod_address,
+                                         &rec_buf[i*2],2)) {
+                               printk(KERN_WARNING "or51211:Load DVR Error 7"
+                                      " - %d\n",i);
+                               return -1;
+                       }
+                       /* If we didn't receive the right index, try again */
+                       if ((int)rec_buf[i*2+1]!=i+1){
+                         i--;
+                       }
+               }
+               dprintk("read_fwbits %x %x %x %x %x %x %x %x %x %x\n",
+                       rec_buf[0], rec_buf[1], rec_buf[2], rec_buf[3],
+                       rec_buf[4], rec_buf[5], rec_buf[6], rec_buf[7],
+                       rec_buf[8], rec_buf[9]);
+
+               printk(KERN_INFO "or51211: ver TU%02x%02x%02x VSB mode %02x"
+                      " Status %02x\n",
+                      rec_buf[2], rec_buf[4],rec_buf[6],
+                      rec_buf[12],rec_buf[10]);
+
+               rec_buf[0] = 0x04;
+               rec_buf[1] = 0x00;
+               rec_buf[2] = 0x03;
+               rec_buf[3] = 0x00;
+               msleep(20);
+               if (i2c_writebytes(state,state->config->demod_address,
+                                  rec_buf,3)) {
+                       printk(KERN_WARNING "or51211: Load DVR Error 8\n");
+                       return -1;
+               }
+               msleep(20);
+               if (i2c_readbytes(state,state->config->demod_address,
+                                 &rec_buf[8],2)) {
+                       printk(KERN_WARNING "or51211: Load DVR Error 9\n");
+                       return -1;
+               }
+               state->initialized = 1;
+       }
+
+       return 0;
+}
+
+static int or51211_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 or51211_release(struct dvb_frontend* fe)
+{
+       struct or51211_state* state = fe->demodulator_priv;
+       state->config->sleep(fe);
+       kfree(state);
+}
+
+static struct dvb_frontend_ops or51211_ops;
+
+struct dvb_frontend* or51211_attach(const struct or51211_config* config,
+                                   struct i2c_adapter* i2c)
+{
+       struct or51211_state* state = NULL;
+
+       /* Allocate memory for the internal state */
+       state = kmalloc(sizeof(struct or51211_state), GFP_KERNEL);
+       if (state == NULL)
+               goto error;
+
+       /* Setup the state */
+       state->config = config;
+       state->i2c = i2c;
+       memcpy(&state->ops, &or51211_ops, sizeof(struct dvb_frontend_ops));
+       state->initialized = 0;
+       state->current_frequency = 0;
+
+       /* Create dvb_frontend */
+       state->frontend.ops = &state->ops;
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error:
+       kfree(state);
+       return NULL;
+}
+
+static struct dvb_frontend_ops or51211_ops = {
+
+       .info = {
+               .name               = "Oren OR51211 VSB Frontend",
+               .type               = FE_ATSC,
+               .frequency_min      = 44000000,
+               .frequency_max      = 958000000,
+               .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 = or51211_release,
+
+       .init = or51211_init,
+       .sleep = or51211_sleep,
+
+       .set_frontend = or51211_set_parameters,
+       .get_tune_settings = or51211_get_tune_settings,
+
+       .read_status = or51211_read_status,
+       .read_ber = or51211_read_ber,
+       .read_signal_strength = or51211_read_signal_strength,
+       .read_snr = or51211_read_snr,
+       .read_ucblocks = or51211_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Oren OR51211 VSB [pcHDTV HD-2000] Demodulator Driver");
+MODULE_AUTHOR("Kirk Lapray");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(or51211_attach);
+
diff --git a/drivers/media/dvb/frontends/or51211.h b/drivers/media/dvb/frontends/or51211.h
new file mode 100644 (file)
index 0000000..13a5a3a
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ *    Support for OR51211 (pcHDTV HD-2000) - VSB
+ *
+ *    Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.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 OR51211_H
+#define OR51211_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct or51211_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);
+       void (*setmode)(struct dvb_frontend * fe, int mode);
+       void (*reset)(struct dvb_frontend * fe);
+       void (*sleep)(struct dvb_frontend * fe);
+};
+
+extern struct dvb_frontend* or51211_attach(const struct or51211_config* config,
+                                          struct i2c_adapter* i2c);
+
+#endif // OR51211_H
+
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
new file mode 100644 (file)
index 0000000..af6ad8c
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * $Id: cx88-input.c,v 1.9 2005/03/04 09:12:23 kraxel Exp $
+ *
+ * Device driver for GPIO attached remote control interfaces
+ * on Conexant 2388x based TV/DVB cards.
+ *
+ * Copyright (c) 2003 Pavel Machek
+ * Copyright (c) 2004 Gerd Knorr
+ * Copyright (c) 2004 Chris Pascoe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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/delay.h>
+#include <linux/input.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <media/ir-common.h>
+
+#include "cx88.h"
+
+/* ---------------------------------------------------------------------- */
+
+/* DigitalNow DNTV Live DVB-T Remote */
+static IR_KEYTAB_TYPE ir_codes_dntv_live_dvb_t[IR_KEYTAB_SIZE] = {
+       [ 0x00 ] = KEY_ESC,         // 'go up a level?'
+       [ 0x01 ] = KEY_KP1,         // '1'
+       [ 0x02 ] = KEY_KP2,         // '2'
+       [ 0x03 ] = KEY_KP3,         // '3'
+       [ 0x04 ] = KEY_KP4,         // '4'
+       [ 0x05 ] = KEY_KP5,         // '5'
+       [ 0x06 ] = KEY_KP6,         // '6'
+       [ 0x07 ] = KEY_KP7,         // '7'
+       [ 0x08 ] = KEY_KP8,         // '8'
+       [ 0x09 ] = KEY_KP9,         // '9'
+       [ 0x0a ] = KEY_KP0,         // '0'
+       [ 0x0b ] = KEY_TUNER,       // 'tv/fm'
+       [ 0x0c ] = KEY_SEARCH,      // 'scan'
+       [ 0x0d ] = KEY_STOP,        // 'stop'
+       [ 0x0e ] = KEY_PAUSE,       // 'pause'
+       [ 0x0f ] = KEY_LIST,        // 'source'
+
+       [ 0x10 ] = KEY_MUTE,        // 'mute'
+       [ 0x11 ] = KEY_REWIND,      // 'backward <<'
+       [ 0x12 ] = KEY_POWER,       // 'power'
+       [ 0x13 ] = KEY_S,           // 'snap'
+       [ 0x14 ] = KEY_AUDIO,       // 'stereo'
+       [ 0x15 ] = KEY_CLEAR,       // 'reset'
+       [ 0x16 ] = KEY_PLAY,        // 'play'
+       [ 0x17 ] = KEY_ENTER,       // 'enter'
+       [ 0x18 ] = KEY_ZOOM,        // 'full screen'
+       [ 0x19 ] = KEY_FASTFORWARD, // 'forward >>'
+       [ 0x1a ] = KEY_CHANNELUP,   // 'channel +'
+       [ 0x1b ] = KEY_VOLUMEUP,    // 'volume +'
+       [ 0x1c ] = KEY_INFO,        // 'preview'
+       [ 0x1d ] = KEY_RECORD,      // 'record'
+       [ 0x1e ] = KEY_CHANNELDOWN, // 'channel -'
+       [ 0x1f ] = KEY_VOLUMEDOWN,  // 'volume -'
+};
+
+/* ---------------------------------------------------------------------- */
+
+/* IO-DATA BCTV7E Remote */
+static IR_KEYTAB_TYPE ir_codes_iodata_bctv7e[IR_KEYTAB_SIZE] = {
+       [ 0x40 ] = KEY_TV,              // TV
+       [ 0x20 ] = KEY_RADIO,           // FM
+       [ 0x60 ] = KEY_EPG,             // EPG
+       [ 0x00 ] = KEY_POWER,           // power
+
+       [ 0x50 ] = KEY_KP1,             // 1
+       [ 0x30 ] = KEY_KP2,             // 2
+       [ 0x70 ] = KEY_KP3,             // 3
+       [ 0x10 ] = KEY_L,               // Live
+
+       [ 0x48 ] = KEY_KP4,             // 4
+       [ 0x28 ] = KEY_KP5,             // 5
+       [ 0x68 ] = KEY_KP6,             // 6
+       [ 0x08 ] = KEY_T,               // Time Shift
+
+       [ 0x58 ] = KEY_KP7,             // 7
+       [ 0x38 ] = KEY_KP8,             // 8
+       [ 0x78 ] = KEY_KP9,             // 9
+       [ 0x18 ] = KEY_PLAYPAUSE,       // Play
+
+       [ 0x44 ] = KEY_KP0,             // 10
+       [ 0x24 ] = KEY_ENTER,           // 11
+       [ 0x64 ] = KEY_ESC,             // 12
+       [ 0x04 ] = KEY_M,               // Multi
+
+       [ 0x54 ] = KEY_VIDEO,           // VIDEO
+       [ 0x34 ] = KEY_CHANNELUP,       // channel +
+       [ 0x74 ] = KEY_VOLUMEUP,        // volume +
+       [ 0x14 ] = KEY_MUTE,            // Mute
+
+       [ 0x4c ] = KEY_S,               // SVIDEO
+       [ 0x2c ] = KEY_CHANNELDOWN,     // channel -
+       [ 0x6c ] = KEY_VOLUMEDOWN,      // volume -
+       [ 0x0c ] = KEY_ZOOM,            // Zoom
+
+       [ 0x5c ] = KEY_PAUSE,           // pause
+       [ 0x3c ] = KEY_C,               // || (red)
+       [ 0x7c ] = KEY_RECORD,          // recording
+       [ 0x1c ] = KEY_STOP,            // stop
+
+       [ 0x41 ] = KEY_REWIND,          // backward <<
+       [ 0x21 ] = KEY_PLAY,            // play
+       [ 0x61 ] = KEY_FASTFORWARD,     // forward >>
+       [ 0x01 ] = KEY_NEXT,            // skip >|
+};
+
+/* ---------------------------------------------------------------------- */
+
+struct cx88_IR {
+       struct cx88_core        *core;
+       struct input_dev        input;
+       struct ir_input_state   ir;
+       char                    name[32];
+       char                    phys[32];
+
+       /* sample from gpio pin 16 */
+       int                     sampling;
+       u32                     samples[16];
+       int                     scount;
+       unsigned long           release;
+
+       /* poll external decoder */
+       int                     polling;
+       struct work_struct      work;
+       struct timer_list       timer;
+       u32                     gpio_addr;
+       u32                     last_gpio;
+       u32                     mask_keycode;
+       u32                     mask_keydown;
+       u32                     mask_keyup;
+};
+
+static int ir_debug = 0;
+module_param(ir_debug, int, 0644);    /* debug level [IR] */
+MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
+
+#define ir_dprintk(fmt, arg...)        if (ir_debug) \
+       printk(KERN_DEBUG "%s IR: " fmt , ir->core->name, ## arg)
+
+/* ---------------------------------------------------------------------- */
+
+static void cx88_ir_handle_key(struct cx88_IR *ir)
+{
+       struct cx88_core *core = ir->core;
+       u32 gpio, data;
+
+       /* read gpio value */
+       gpio = cx_read(ir->gpio_addr);
+       if (ir->polling) {
+               if (ir->last_gpio == gpio)
+                       return;
+               ir->last_gpio = gpio;
+       }
+
+       /* extract data */
+       data = ir_extract_bits(gpio, ir->mask_keycode);
+       ir_dprintk("irq gpio=0x%x code=%d | %s%s%s\n",
+               gpio, data,
+               ir->polling               ? "poll"  : "irq",
+               (gpio & ir->mask_keydown) ? " down" : "",
+               (gpio & ir->mask_keyup)   ? " up"   : "");
+
+       if (ir->mask_keydown) {
+               /* bit set on keydown */
+               if (gpio & ir->mask_keydown) {
+                       ir_input_keydown(&ir->input,&ir->ir,data,data);
+               } else {
+                       ir_input_nokey(&ir->input,&ir->ir);
+               }
+
+       } else if (ir->mask_keyup) {
+               /* bit cleared on keydown */
+               if (0 == (gpio & ir->mask_keyup)) {
+                       ir_input_keydown(&ir->input,&ir->ir,data,data);
+               } else {
+                       ir_input_nokey(&ir->input,&ir->ir);
+               }
+
+       } else {
+               /* can't distinguish keydown/up :-/ */
+               ir_input_keydown(&ir->input,&ir->ir,data,data);
+               ir_input_nokey(&ir->input,&ir->ir);
+       }
+}
+
+static void ir_timer(unsigned long data)
+{
+       struct cx88_IR *ir = (struct cx88_IR*)data;
+
+       schedule_work(&ir->work);
+}
+
+static void cx88_ir_work(void *data)
+{
+       struct cx88_IR *ir = data;
+       unsigned long timeout;
+
+       cx88_ir_handle_key(ir);
+       timeout = jiffies + (ir->polling * HZ / 1000);
+       mod_timer(&ir->timer, timeout);
+}
+
+/* ---------------------------------------------------------------------- */
+
+int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
+{
+       struct cx88_IR *ir;
+       IR_KEYTAB_TYPE *ir_codes = NULL;
+       int ir_type = IR_TYPE_OTHER;
+
+       ir = kmalloc(sizeof(*ir),GFP_KERNEL);
+       if (NULL == ir)
+               return -ENOMEM;
+       memset(ir,0,sizeof(*ir));
+
+       /* detect & configure */
+       switch (core->board) {
+       case CX88_BOARD_DNTV_LIVE_DVB_T:
+               ir_codes         = ir_codes_dntv_live_dvb_t;
+               ir->gpio_addr    = MO_GP1_IO;
+               ir->mask_keycode = 0x1f;
+               ir->mask_keyup   = 0x60;
+               ir->polling      = 50; // ms
+               break;
+       case CX88_BOARD_HAUPPAUGE:
+       case CX88_BOARD_HAUPPAUGE_DVB_T1:
+               ir_codes         = ir_codes_hauppauge_new;
+               ir_type          = IR_TYPE_RC5;
+               ir->sampling     = 1;
+               break;
+       case CX88_BOARD_WINFAST2000XP_EXPERT:
+               ir_codes         = ir_codes_winfast;
+               ir->gpio_addr    = MO_GP0_IO;
+               ir->mask_keycode = 0x8f8;
+               ir->mask_keyup   = 0x100;
+               ir->polling      = 1; // ms
+               break;
+       case CX88_BOARD_IODATA_GVBCTV7E:
+               ir_codes         = ir_codes_iodata_bctv7e;
+               ir->gpio_addr    = MO_GP0_IO;
+               ir->mask_keycode = 0xfd;
+               ir->mask_keydown = 0x02;
+               ir->polling      = 5; // ms
+               break;
+       }
+       if (NULL == ir_codes) {
+               kfree(ir);
+               return -ENODEV;
+       }
+
+       /* init input device */
+       snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)",
+                cx88_boards[core->board].name);
+       snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
+                pci_name(pci));
+
+       ir_input_init(&ir->input, &ir->ir, ir_type, ir_codes);
+       ir->input.name = ir->name;
+       ir->input.phys = ir->phys;
+       ir->input.id.bustype = BUS_PCI;
+       ir->input.id.version = 1;
+       if (pci->subsystem_vendor) {
+               ir->input.id.vendor  = pci->subsystem_vendor;
+               ir->input.id.product = pci->subsystem_device;
+       } else {
+               ir->input.id.vendor  = pci->vendor;
+               ir->input.id.product = pci->device;
+       }
+
+       /* record handles to ourself */
+       ir->core = core;
+       core->ir = ir;
+
+       if (ir->polling) {
+               INIT_WORK(&ir->work, cx88_ir_work, ir);
+               init_timer(&ir->timer);
+               ir->timer.function = ir_timer;
+               ir->timer.data     = (unsigned long)ir;
+               schedule_work(&ir->work);
+       }
+       if (ir->sampling) {
+               core->pci_irqmask |= (1<<18);   // IR_SMP_INT
+               cx_write(MO_DDS_IO, 0xa80a80);  // 4 kHz sample rate
+               cx_write(MO_DDSCFG_IO,   0x5);  // enable
+       }
+
+       /* all done */
+       input_register_device(&ir->input);
+       printk("%s: registered IR remote control\n", core->name);
+
+       return 0;
+}
+
+int cx88_ir_fini(struct cx88_core *core)
+{
+       struct cx88_IR *ir = core->ir;
+
+       /* skip detach on non attached boards */
+       if (NULL == ir)
+               return 0;
+
+       if (ir->polling) {
+               del_timer(&ir->timer);
+               flush_scheduled_work();
+       }
+
+       input_unregister_device(&ir->input);
+       kfree(ir);
+
+       /* done */
+       core->ir = NULL;
+       return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void cx88_ir_irq(struct cx88_core *core)
+{
+       struct cx88_IR *ir = core->ir;
+       u32 samples,rc5;
+       int i;
+
+       if (NULL == ir)
+               return;
+       if (!ir->sampling)
+               return;
+
+       samples = cx_read(MO_SAMPLE_IO);
+       if (0 != samples  &&  0xffffffff != samples) {
+               /* record sample data */
+               if (ir->scount < ARRAY_SIZE(ir->samples))
+                       ir->samples[ir->scount++] = samples;
+               return;
+       }
+       if (!ir->scount) {
+               /* nothing to sample */
+               if (ir->ir.keypressed && time_after(jiffies,ir->release))
+                       ir_input_nokey(&ir->input,&ir->ir);
+               return;
+       }
+
+       /* have a complete sample */
+       if (ir->scount < ARRAY_SIZE(ir->samples))
+               ir->samples[ir->scount++] = samples;
+       for (i = 0; i < ir->scount; i++)
+               ir->samples[i] = ~ir->samples[i];
+       if (ir_debug)
+               ir_dump_samples(ir->samples,ir->scount);
+
+       /* decode it */
+       switch (core->board) {
+       case CX88_BOARD_HAUPPAUGE:
+       case CX88_BOARD_HAUPPAUGE_DVB_T1:
+               rc5 = ir_decode_biphase(ir->samples,ir->scount,5,7);
+               ir_dprintk("biphase decoded: %x\n",rc5);
+               if ((rc5 & 0xfffff000) != 0x3000)
+                       break;
+               ir_input_keydown(&ir->input, &ir->ir, rc5 & 0x3f, rc5);
+               ir->release = jiffies + msecs_to_jiffies(120);
+               break;
+       }
+
+       ir->scount = 0;
+       return;
+}
+
+/* ---------------------------------------------------------------------- */
+
+MODULE_AUTHOR("Gerd Knorr, Pavel Machek, Chris Pascoe");
+MODULE_DESCRIPTION("input driver for cx88 GPIO-based IR remote controls");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/tda8290.c b/drivers/media/video/tda8290.c
new file mode 100644 (file)
index 0000000..b27cc34
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * $Id: tda8290.c,v 1.7 2005/03/07 12:01:51 kraxel Exp $
+ *
+ * i2c tv tuner chip device driver
+ * controls the philips tda8290+75 tuner chip combo.
+ */
+#include <linux/i2c.h>
+#include <linux/videodev.h>
+#include <linux/delay.h>
+#include <media/tuner.h>
+
+/* ---------------------------------------------------------------------- */
+
+struct freq_entry {
+       u16     freq;
+       u8      value;
+};
+
+static struct freq_entry band_table[] = {
+       { 0x2DF4, 0x1C },
+       { 0x2574, 0x14 },
+       { 0x22B4, 0x0C },
+       { 0x20D4, 0x0B },
+       { 0x1E74, 0x3B },
+       { 0x1C34, 0x33 },
+       { 0x16F4, 0x5B },
+       { 0x1454, 0x53 },
+       { 0x12D4, 0x52 },
+       { 0x1034, 0x4A },
+       { 0x0EE4, 0x7A },
+       { 0x0D34, 0x72 },
+       { 0x0B54, 0x9A },
+       { 0x0914, 0x91 },
+       { 0x07F4, 0x89 },
+       { 0x0774, 0xB9 },
+       { 0x067B, 0xB1 },
+       { 0x0634, 0xD9 },
+       { 0x05A4, 0xD8 },       // FM radio
+       { 0x0494, 0xD0 },
+       { 0x03BC, 0xC8 },
+       { 0x0394, 0xF8 },       // 57250000 Hz
+       { 0x0000, 0xF0 },       // 0
+};
+
+static struct freq_entry div_table[] = {
+       { 0x1C34, 3 },
+       { 0x0D34, 2 },
+       { 0x067B, 1 },
+        { 0x0000, 0 },
+};
+
+static struct freq_entry agc_table[] = {
+       { 0x22B4, 0x8F },
+       { 0x0B54, 0x9F },
+       { 0x09A4, 0x8F },
+       { 0x0554, 0x9F },
+       { 0x0000, 0xBF },
+};
+
+static __u8 get_freq_entry( struct freq_entry* table, __u16 freq)
+{
+       while(table->freq && table->freq > freq)
+               table++;
+       return table->value;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static unsigned char i2c_enable_bridge[2] =    { 0x21, 0xC0 };
+static unsigned char i2c_disable_bridge[2] =   { 0x21, 0x80 };
+static unsigned char i2c_init_tda8275[14] =    { 0x00, 0x00, 0x00, 0x00,
+                                                 0x7C, 0x04, 0xA3, 0x3F,
+                                                 0x2A, 0x04, 0xFF, 0x00,
+                                                 0x00, 0x40 };
+static unsigned char i2c_set_VS[2] =           { 0x30, 0x6F };
+static unsigned char i2c_set_GP01_CF[2] =      { 0x20, 0x0B };
+static unsigned char i2c_tda8290_reset[2] =    { 0x00, 0x00 };
+static unsigned char i2c_gainset_off[2] =      { 0x28, 0x14 };
+static unsigned char i2c_gainset_on[2] =       { 0x28, 0x54 };
+static unsigned char i2c_agc3_00[2] =          { 0x80, 0x00 };
+static unsigned char i2c_agc2_BF[2] =          { 0x60, 0xBF };
+static unsigned char i2c_cb1_D2[2] =           { 0x30, 0xD2 };
+static unsigned char i2c_cb1_56[2] =           { 0x30, 0x56 };
+static unsigned char i2c_cb1_52[2] =           { 0x30, 0x52 };
+static unsigned char i2c_cb1_50[2] =           { 0x30, 0x50 };
+static unsigned char i2c_agc2_7F[2] =          { 0x60, 0x7F };
+static unsigned char i2c_agc3_08[2] =          { 0x80, 0x08 };
+
+static struct i2c_msg i2c_msg_init[] = {
+       { I2C_ADDR_TDA8275, 0, ARRAY_SIZE(i2c_init_tda8275), i2c_init_tda8275 },
+       { I2C_ADDR_TDA8290, 0, ARRAY_SIZE(i2c_disable_bridge), i2c_disable_bridge },
+       { I2C_ADDR_TDA8290, 0, ARRAY_SIZE(i2c_set_VS), i2c_set_VS },
+       { I2C_ADDR_TDA8290, 0, ARRAY_SIZE(i2c_set_GP01_CF), i2c_set_GP01_CF },
+};
+
+static struct i2c_msg i2c_msg_prolog[] = {
+//     { I2C_ADDR_TDA8290, 0, ARRAY_SIZE(i2c_easy_mode), i2c_easy_mode },
+       { I2C_ADDR_TDA8290, 0, ARRAY_SIZE(i2c_gainset_off), i2c_gainset_off },
+       { I2C_ADDR_TDA8290, 0, ARRAY_SIZE(i2c_tda8290_reset), i2c_tda8290_reset },
+       { I2C_ADDR_TDA8290, 0, ARRAY_SIZE(i2c_enable_bridge), i2c_enable_bridge },
+};
+
+static struct i2c_msg i2c_msg_config[] = {
+//     { I2C_ADDR_TDA8275, 0, ARRAY_SIZE(i2c_set_freq), i2c_set_freq },
+       { I2C_ADDR_TDA8275, 0, ARRAY_SIZE(i2c_agc3_00), i2c_agc3_00 },
+       { I2C_ADDR_TDA8275, 0, ARRAY_SIZE(i2c_agc2_BF), i2c_agc2_BF },
+       { I2C_ADDR_TDA8275, 0, ARRAY_SIZE(i2c_cb1_D2), i2c_cb1_D2 },
+       { I2C_ADDR_TDA8275, 0, ARRAY_SIZE(i2c_cb1_56), i2c_cb1_56 },
+       { I2C_ADDR_TDA8275, 0, ARRAY_SIZE(i2c_cb1_52), i2c_cb1_52 },
+};
+
+static struct i2c_msg i2c_msg_epilog[] = {
+       { I2C_ADDR_TDA8275, 0, ARRAY_SIZE(i2c_cb1_50), i2c_cb1_50 },
+       { I2C_ADDR_TDA8275, 0, ARRAY_SIZE(i2c_agc2_7F), i2c_agc2_7F },
+       { I2C_ADDR_TDA8275, 0, ARRAY_SIZE(i2c_agc3_08), i2c_agc3_08 },
+       { I2C_ADDR_TDA8290, 0, ARRAY_SIZE(i2c_disable_bridge), i2c_disable_bridge },
+       { I2C_ADDR_TDA8290, 0, ARRAY_SIZE(i2c_gainset_on), i2c_gainset_on },
+};
+
+static int tda8290_tune(struct i2c_client *c)
+{
+       struct tuner *t = i2c_get_clientdata(c);
+       struct i2c_msg easy_mode =
+               { I2C_ADDR_TDA8290, 0, 2, t->i2c_easy_mode };
+       struct i2c_msg set_freq =
+               { I2C_ADDR_TDA8275, 0, 8, t->i2c_set_freq  };
+
+       i2c_transfer(c->adapter, &easy_mode,      1);
+       i2c_transfer(c->adapter, i2c_msg_prolog, ARRAY_SIZE(i2c_msg_prolog));
+
+       i2c_transfer(c->adapter, &set_freq,       1);
+       i2c_transfer(c->adapter, i2c_msg_config, ARRAY_SIZE(i2c_msg_config));
+
+       msleep(550);
+       i2c_transfer(c->adapter, i2c_msg_epilog, ARRAY_SIZE(i2c_msg_epilog));
+       return 0;
+}
+
+static void set_frequency(struct tuner *t, u16 ifc)
+{
+       u32 N = (((t->freq<<3)+ifc)&0x3fffc);
+
+       N = N >> get_freq_entry(div_table, t->freq);
+       t->i2c_set_freq[0] = 0;
+       t->i2c_set_freq[1] = (unsigned char)(N>>8);
+       t->i2c_set_freq[2] = (unsigned char) N;
+       t->i2c_set_freq[3] = 0x40;
+       t->i2c_set_freq[4] = 0x52;
+       t->i2c_set_freq[5] = get_freq_entry(band_table, t->freq);
+       t->i2c_set_freq[6] = get_freq_entry(agc_table,  t->freq);
+       t->i2c_set_freq[7] = 0x8f;
+}
+
+#define V4L2_STD_MN    (V4L2_STD_PAL_M|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc|V4L2_STD_NTSC)
+#define V4L2_STD_B     (V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_SECAM_B)
+#define V4L2_STD_GH    (V4L2_STD_PAL_G|V4L2_STD_PAL_H|V4L2_STD_SECAM_G|V4L2_STD_SECAM_H)
+#define V4L2_STD_DK    (V4L2_STD_PAL_DK|V4L2_STD_SECAM_DK)
+
+static void set_audio(struct tuner *t)
+{
+       t->i2c_easy_mode[0] = 0x01;
+
+       if (t->std & V4L2_STD_MN)
+               t->i2c_easy_mode[1] = 0x01;
+       else if (t->std & V4L2_STD_B)
+               t->i2c_easy_mode[1] = 0x02;
+       else if (t->std & V4L2_STD_GH)
+               t->i2c_easy_mode[1] = 0x04;
+       else if (t->std & V4L2_STD_PAL_I)
+               t->i2c_easy_mode[1] = 0x08;
+       else if (t->std & V4L2_STD_DK)
+               t->i2c_easy_mode[1] = 0x10;
+       else if (t->std & V4L2_STD_SECAM_L)
+               t->i2c_easy_mode[1] = 0x20;
+}
+
+static void set_tv_freq(struct i2c_client *c, unsigned int freq)
+{
+       struct tuner *t = i2c_get_clientdata(c);
+
+       set_audio(t);
+       set_frequency(t, 864);
+       tda8290_tune(c);
+}
+
+static void set_radio_freq(struct i2c_client *c, unsigned int freq)
+{
+       struct tuner *t = i2c_get_clientdata(c);
+       set_frequency(t, 704);
+       tda8290_tune(c);
+}
+
+static int has_signal(struct i2c_client *c)
+{
+       unsigned char i2c_get_afc[1] = { 0x1B };
+       unsigned char afc = 0;
+
+       i2c_master_send(c, i2c_get_afc, ARRAY_SIZE(i2c_get_afc));
+       i2c_master_recv(c, &afc, 1);
+       return (afc & 0x80)? 65535:0;
+}
+
+int tda8290_init(struct i2c_client *c)
+{
+       struct tuner *t = i2c_get_clientdata(c);
+
+       strlcpy(c->name, "tda8290+75", sizeof(c->name));
+       tuner_info("tuner: type set to %s\n", c->name);
+       t->tv_freq    = set_tv_freq;
+       t->radio_freq = set_radio_freq;
+       t->has_signal = has_signal;
+
+       i2c_master_send(c, i2c_enable_bridge, ARRAY_SIZE(i2c_enable_bridge));
+       i2c_transfer(c->adapter, i2c_msg_init, ARRAY_SIZE(i2c_msg_init));
+       return 0;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
new file mode 100644 (file)
index 0000000..6212388
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+ * $Id: tuner-core.c,v 1.5 2005/02/15 15:59:35 kraxel Exp $
+ *
+ * i2c tv tuner chip device driver
+ * core core, i.e. kernel interfaces, registering and so on
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/videodev.h>
+#include <linux/init.h>
+
+#include <media/tuner.h>
+#include <media/audiochip.h>
+
+#define UNSET (-1U)
+
+/* standard i2c insmod options */
+static unsigned short normal_i2c[] = {
+       0x4b, /* tda8290 */
+       I2C_CLIENT_END
+};
+static unsigned short normal_i2c_range[] = {
+       0x60, 0x6f,
+       I2C_CLIENT_END
+};
+I2C_CLIENT_INSMOD;
+
+/* insmod options used at init time => read/only */
+static unsigned int addr  =  0;
+module_param(addr, int, 0444);
+
+/* insmod options used at runtime => read/write */
+unsigned int tuner_debug   = 0;
+module_param(tuner_debug,       int, 0644);
+
+static unsigned int tv_range[2]    = { 44, 958 };
+static unsigned int radio_range[2] = { 65, 108 };
+
+module_param_array(tv_range,    int, NULL, 0644);
+module_param_array(radio_range, int, NULL, 0644);
+
+MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
+MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
+MODULE_LICENSE("GPL");
+
+static int this_adap;
+
+static struct i2c_driver driver;
+static struct i2c_client client_template;
+
+/* ---------------------------------------------------------------------- */
+
+// Set tuner frequency,  freq in Units of 62.5kHz = 1/16MHz
+static void set_tv_freq(struct i2c_client *c, unsigned int freq)
+{
+       struct tuner *t = i2c_get_clientdata(c);
+
+       if (t->type == UNSET) {
+               tuner_info("tuner type not set\n");
+               return;
+       }
+       if (NULL == t->tv_freq) {
+               tuner_info("Huh? tv_set is NULL?\n");
+               return;
+       }
+       if (freq < tv_range[0]*16 || freq > tv_range[1]*16) {
+               /* FIXME: better do that chip-specific, but
+                  right now we don't have that in the config
+                  struct and this way is still better than no
+                  check at all */
+               tuner_info("TV freq (%d.%02d) out of range (%d-%d)\n",
+                          freq/16,freq%16*100/16,tv_range[0],tv_range[1]);
+               return;
+       }
+       t->tv_freq(c,freq);
+}
+
+static void set_radio_freq(struct i2c_client *c, unsigned int freq)
+{
+       struct tuner *t = i2c_get_clientdata(c);
+
+       if (t->type == UNSET) {
+               tuner_info("tuner type not set\n");
+               return;
+       }
+       if (NULL == t->radio_freq) {
+               tuner_info("no radio tuning for this one, sorry.\n");
+               return;
+       }
+       if (freq < radio_range[0]*16 || freq > radio_range[1]*16) {
+               tuner_info("radio freq (%d.%02d) out of range (%d-%d)\n",
+                          freq/16,freq%16*100/16,
+                          radio_range[0],radio_range[1]);
+               return;
+       }
+       t->radio_freq(c,freq);
+}
+
+static void set_freq(struct i2c_client *c, unsigned long freq)
+{
+       struct tuner *t = i2c_get_clientdata(c);
+
+       switch (t->mode) {
+       case V4L2_TUNER_RADIO:
+               tuner_dbg("radio freq set to %lu.%02lu\n",
+                         freq/16,freq%16*100/16);
+               set_radio_freq(c,freq);
+               break;
+       case V4L2_TUNER_ANALOG_TV:
+       case V4L2_TUNER_DIGITAL_TV:
+               tuner_dbg("tv freq set to %lu.%02lu\n",
+                         freq/16,freq%16*100/16);
+               set_tv_freq(c, freq);
+               break;
+       }
+       t->freq = freq;
+}
+
+static void set_type(struct i2c_client *c, unsigned int type)
+{
+       struct tuner *t = i2c_get_clientdata(c);
+
+       /* sanity check */
+       if (type == UNSET  ||  type == TUNER_ABSENT)
+               return;
+       if (type >= tuner_count)
+               return;
+
+       if (NULL == t->i2c.dev.driver) {
+               /* not registered yet */
+               t->type = type;
+               return;
+       }
+       if (t->initialized)
+               /* run only once */
+               return;
+
+       t->initialized = 1;
+       t->type = type;
+       switch (t->type) {
+       case TUNER_MT2032:
+               microtune_init(c);
+               break;
+       case TUNER_PHILIPS_TDA8290:
+               tda8290_init(c);
+               break;
+       default:
+               default_tuner_init(c);
+               break;
+       }
+}
+
+static char pal[] = "-";
+module_param_string(pal, pal, sizeof(pal), 0644);
+
+static int tuner_fixup_std(struct tuner *t)
+{
+       if ((t->std & V4L2_STD_PAL) == V4L2_STD_PAL) {
+               /* get more precise norm info from insmod option */
+               switch (pal[0]) {
+               case 'b':
+               case 'B':
+               case 'g':
+               case 'G':
+                       tuner_dbg("insmod fixup: PAL => PAL-BG\n");
+                       t->std = V4L2_STD_PAL_BG;
+                       break;
+               case 'i':
+               case 'I':
+                       tuner_dbg("insmod fixup: PAL => PAL-I\n");
+                       t->std = V4L2_STD_PAL_I;
+                       break;
+               case 'd':
+               case 'D':
+               case 'k':
+               case 'K':
+                       tuner_dbg("insmod fixup: PAL => PAL-DK\n");
+                       t->std = V4L2_STD_PAL_DK;
+                       break;
+               }
+       }
+       return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
+{
+       struct tuner *t;
+
+       if (this_adap > 0)
+               return -1;
+       this_adap++;
+
+        client_template.adapter = adap;
+        client_template.addr = addr;
+
+        t = kmalloc(sizeof(struct tuner),GFP_KERNEL);
+        if (NULL == t)
+                return -ENOMEM;
+        memset(t,0,sizeof(struct tuner));
+        memcpy(&t->i2c,&client_template,sizeof(struct i2c_client));
+       i2c_set_clientdata(&t->i2c, t);
+       t->type       = UNSET;
+       t->radio_if2  = 10700*1000; // 10.7MHz - FM radio
+
+        i2c_attach_client(&t->i2c);
+       tuner_info("chip found @ 0x%x (%s)\n",
+                  addr << 1, adap->name);
+       set_type(&t->i2c, t->type);
+       return 0;
+}
+
+static int tuner_probe(struct i2c_adapter *adap)
+{
+       if (0 != addr) {
+               normal_i2c[0]       = addr;
+               normal_i2c_range[0] = addr;
+               normal_i2c_range[1] = addr;
+       }
+       this_adap = 0;
+
+       if (adap->class & I2C_CLASS_TV_ANALOG)
+               return i2c_probe(adap, &addr_data, tuner_attach);
+       return 0;
+}
+
+static int tuner_detach(struct i2c_client *client)
+{
+       struct tuner *t = i2c_get_clientdata(client);
+
+       i2c_detach_client(&t->i2c);
+       kfree(t);
+       return 0;
+}
+
+#define SWITCH_V4L2    if (!t->using_v4l2 && tuner_debug) \
+                         tuner_info("switching to v4l2\n"); \
+                         t->using_v4l2 = 1;
+#define CHECK_V4L2     if (t->using_v4l2) { if (tuner_debug) \
+                         tuner_info("ignore v4l1 call\n"); \
+                         return 0; }
+
+static int
+tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+       struct tuner *t = i2c_get_clientdata(client);
+        unsigned int *iarg = (int*)arg;
+
+        switch (cmd) {
+
+       /* --- configuration --- */
+       case TUNER_SET_TYPE:
+               set_type(client,*iarg);
+               break;
+       case AUDC_SET_RADIO:
+               if (V4L2_TUNER_RADIO != t->mode) {
+                       set_tv_freq(client,400 * 16);
+                       t->mode = V4L2_TUNER_RADIO;
+               }
+               break;
+       case AUDC_CONFIG_PINNACLE:
+               switch (*iarg) {
+               case 2:
+                       tuner_dbg("pinnacle pal\n");
+                       t->radio_if2 = 33300 * 1000;
+                       break;
+               case 3:
+                       tuner_dbg("pinnacle ntsc\n");
+                       t->radio_if2 = 41300 * 1000;
+                       break;
+               }
+                break;
+
+       /* --- v4l ioctls --- */
+       /* take care: bttv does userspace copying, we'll get a
+          kernel pointer here... */
+       case VIDIOCSCHAN:
+       {
+               static const v4l2_std_id map[] = {
+                       [ VIDEO_MODE_PAL   ] = V4L2_STD_PAL,
+                       [ VIDEO_MODE_NTSC  ] = V4L2_STD_NTSC_M,
+                       [ VIDEO_MODE_SECAM ] = V4L2_STD_SECAM,
+                       [ 4 /* bttv */     ] = V4L2_STD_PAL_M,
+                       [ 5 /* bttv */     ] = V4L2_STD_PAL_N,
+                       [ 6 /* bttv */     ] = V4L2_STD_NTSC_M_JP,
+               };
+               struct video_channel *vc = arg;
+
+               CHECK_V4L2;
+               t->mode = V4L2_TUNER_ANALOG_TV;
+               if (vc->norm < ARRAY_SIZE(map))
+                       t->std = map[vc->norm];
+               tuner_fixup_std(t);
+               if (t->freq)
+                       set_tv_freq(client,t->freq);
+               return 0;
+       }
+       case VIDIOCSFREQ:
+       {
+               unsigned long *v = arg;
+
+               CHECK_V4L2;
+               set_freq(client,*v);
+               return 0;
+       }
+       case VIDIOCGTUNER:
+       {
+               struct video_tuner *vt = arg;
+
+               CHECK_V4L2;
+               if (V4L2_TUNER_RADIO == t->mode  &&  t->has_signal)
+                       vt->signal = t->has_signal(client);
+               return 0;
+       }
+       case VIDIOCGAUDIO:
+       {
+               struct video_audio *va = arg;
+
+               CHECK_V4L2;
+               if (V4L2_TUNER_RADIO == t->mode  &&  t->is_stereo)
+                       va->mode = t->is_stereo(client)
+                               ? VIDEO_SOUND_STEREO
+                               : VIDEO_SOUND_MONO;
+               return 0;
+       }
+
+       case VIDIOC_S_STD:
+       {
+               v4l2_std_id *id = arg;
+
+               SWITCH_V4L2;
+               t->mode = V4L2_TUNER_ANALOG_TV;
+               t->std = *id;
+               tuner_fixup_std(t);
+               if (t->freq)
+                       set_freq(client,t->freq);
+               break;
+       }
+       case VIDIOC_S_FREQUENCY:
+       {
+               struct v4l2_frequency *f = arg;
+
+               SWITCH_V4L2;
+               if (V4L2_TUNER_RADIO == f->type &&
+                   V4L2_TUNER_RADIO != t->mode)
+                       set_tv_freq(client,400*16);
+               t->mode  = f->type;
+               set_freq(client,f->frequency);
+               break;
+       }
+       case VIDIOC_G_FREQUENCY:
+       {
+               struct v4l2_frequency *f = arg;
+
+               SWITCH_V4L2;
+               f->type = t->mode;
+               f->frequency = t->freq;
+               break;
+       }
+       case VIDIOC_G_TUNER:
+       {
+               struct v4l2_tuner *tuner = arg;
+
+               SWITCH_V4L2;
+               if (V4L2_TUNER_RADIO == t->mode  &&  t->has_signal)
+                       tuner->signal = t->has_signal(client);
+               tuner->rangelow = tv_range[0] * 16;
+               tuner->rangehigh = tv_range[1] * 16;
+               break;
+       }
+       default:
+               /* nothing */
+               break;
+       }
+
+       return 0;
+}
+
+static int tuner_suspend(struct device * dev, pm_message_t state, u32 level)
+{
+       struct i2c_client *c = container_of(dev, struct i2c_client, dev);
+       struct tuner *t = i2c_get_clientdata(c);
+
+       tuner_dbg("suspend\n");
+       /* FIXME: power down ??? */
+       return 0;
+}
+
+static int tuner_resume(struct device * dev, u32 level)
+{
+       struct i2c_client *c = container_of(dev, struct i2c_client, dev);
+       struct tuner *t = i2c_get_clientdata(c);
+
+       tuner_dbg("resume\n");
+       if (t->freq)
+               set_freq(c,t->freq);
+       return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_driver driver = {
+       .owner          = THIS_MODULE,
+        .name           = "tuner",
+        .id             = I2C_DRIVERID_TUNER,
+        .flags          = I2C_DF_NOTIFY,
+        .attach_adapter = tuner_probe,
+        .detach_client  = tuner_detach,
+        .command        = tuner_command,
+       .driver = {
+               .suspend = tuner_suspend,
+               .resume  = tuner_resume,
+       },
+};
+static struct i2c_client client_template =
+{
+       I2C_DEVNAME("(tuner unset)"),
+       .flags      = I2C_CLIENT_ALLOW_USE,
+        .driver     = &driver,
+};
+
+static int __init tuner_init_module(void)
+{
+       return i2c_add_driver(&driver);
+}
+
+static void __exit tuner_cleanup_module(void)
+{
+       i2c_del_driver(&driver);
+}
+
+module_init(tuner_init_module);
+module_exit(tuner_cleanup_module);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c
new file mode 100644 (file)
index 0000000..48c6cef
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+ * $Id: tuner-simple.c,v 1.10 2005/03/08 08:38:00 kraxel Exp $
+ *
+ * i2c tv tuner chip device driver
+ * controls all those simple 4-control-bytes style tuners.
+ */
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/videodev.h>
+#include <media/tuner.h>
+
+/* ---------------------------------------------------------------------- */
+
+/* tv standard selection for Temic 4046 FM5
+   this value takes the low bits of control byte 2
+   from datasheet Rev.01, Feb.00
+     standard     BG      I       L       L2      D
+     picture IF   38.9    38.9    38.9    33.95   38.9
+     sound 1      33.4    32.9    32.4    40.45   32.4
+     sound 2      33.16
+     NICAM        33.05   32.348  33.05           33.05
+ */
+#define TEMIC_SET_PAL_I         0x05
+#define TEMIC_SET_PAL_DK        0x09
+#define TEMIC_SET_PAL_L         0x0a // SECAM ?
+#define TEMIC_SET_PAL_L2        0x0b // change IF !
+#define TEMIC_SET_PAL_BG        0x0c
+
+/* tv tuner system standard selection for Philips FQ1216ME
+   this value takes the low bits of control byte 2
+   from datasheet "1999 Nov 16" (supersedes "1999 Mar 23")
+     standard          BG      DK      I       L       L`
+     picture carrier   38.90   38.90   38.90   38.90   33.95
+     colour            34.47   34.47   34.47   34.47   38.38
+     sound 1           33.40   32.40   32.90   32.40   40.45
+     sound 2           33.16   -       -       -       -
+     NICAM             33.05   33.05   32.35   33.05   39.80
+ */
+#define PHILIPS_SET_PAL_I      0x01 /* Bit 2 always zero !*/
+#define PHILIPS_SET_PAL_BGDK   0x09
+#define PHILIPS_SET_PAL_L2     0x0a
+#define PHILIPS_SET_PAL_L      0x0b
+
+/* system switching for Philips FI1216MF MK2
+   from datasheet "1996 Jul 09",
+    standard         BG     L      L'
+    picture carrier  38.90  38.90  33.95
+    colour          34.47  34.37  38.38
+    sound 1          33.40  32.40  40.45
+    sound 2          33.16  -      -
+    NICAM            33.05  33.05  39.80
+ */
+#define PHILIPS_MF_SET_BG      0x01 /* Bit 2 must be zero, Bit 3 is system output */
+#define PHILIPS_MF_SET_PAL_L   0x03 // France
+#define PHILIPS_MF_SET_PAL_L2  0x02 // L'
+
+
+/* ---------------------------------------------------------------------- */
+
+struct tunertype
+{
+       char *name;
+       unsigned char Vendor;
+       unsigned char Type;
+
+       unsigned short thresh1;  /*  band switch VHF_LO <=> VHF_HI  */
+       unsigned short thresh2;  /*  band switch VHF_HI <=> UHF     */
+       unsigned char VHF_L;
+       unsigned char VHF_H;
+       unsigned char UHF;
+       unsigned char config;
+       unsigned short IFPCoff; /* 622.4=16*38.90 MHz PAL,
+                                  732  =16*45.75 NTSCi,
+                                  940  =16*58.75 NTSC-Japan
+                                  704  =16*44    ATSC */
+};
+
+/*
+ *     The floats in the tuner struct are computed at compile time
+ *     by gcc and cast back to integers. Thus we don't violate the
+ *     "no float in kernel" rule.
+ */
+static struct tunertype tuners[] = {
+        { "Temic PAL (4002 FH5)", TEMIC, PAL,
+         16*140.25,16*463.25,0x02,0x04,0x01,0x8e,623},
+       { "Philips PAL_I (FI1246 and compatibles)", Philips, PAL_I,
+         16*140.25,16*463.25,0xa0,0x90,0x30,0x8e,623},
+       { "Philips NTSC (FI1236,FM1236 and compatibles)", Philips, NTSC,
+         16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,732},
+       { "Philips (SECAM+PAL_BG) (FI1216MF, FM1216MF, FR1216MF)", Philips, SECAM,
+         16*168.25,16*447.25,0xA7,0x97,0x37,0x8e,623},
+
+       { "NoTuner", NoTuner, NOTUNER,
+         0,0,0x00,0x00,0x00,0x00,0x00},
+       { "Philips PAL_BG (FI1216 and compatibles)", Philips, PAL,
+         16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,623},
+       { "Temic NTSC (4032 FY5)", TEMIC, NTSC,
+         16*157.25,16*463.25,0x02,0x04,0x01,0x8e,732},
+       { "Temic PAL_I (4062 FY5)", TEMIC, PAL_I,
+         16*170.00,16*450.00,0x02,0x04,0x01,0x8e,623},
+
+       { "Temic NTSC (4036 FY5)", TEMIC, NTSC,
+         16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,732},
+        { "Alps HSBH1", TEMIC, NTSC,
+         16*137.25,16*385.25,0x01,0x02,0x08,0x8e,732},
+        { "Alps TSBE1",TEMIC,PAL,
+         16*137.25,16*385.25,0x01,0x02,0x08,0x8e,732},
+        { "Alps TSBB5", Alps, PAL_I, /* tested (UK UHF) with Modulartech MM205 */
+         16*133.25,16*351.25,0x01,0x02,0x08,0x8e,632},
+
+        { "Alps TSBE5", Alps, PAL, /* untested - data sheet guess. Only IF differs. */
+         16*133.25,16*351.25,0x01,0x02,0x08,0x8e,622},
+        { "Alps TSBC5", Alps, PAL, /* untested - data sheet guess. Only IF differs. */
+         16*133.25,16*351.25,0x01,0x02,0x08,0x8e,608},
+       { "Temic PAL_BG (4006FH5)", TEMIC, PAL,
+         16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
+       { "Alps TSCH6",Alps,NTSC,
+         16*137.25,16*385.25,0x14,0x12,0x11,0x8e,732},
+
+       { "Temic PAL_DK (4016 FY5)",TEMIC,PAL,
+         16*168.25,16*456.25,0xa0,0x90,0x30,0x8e,623},
+       { "Philips NTSC_M (MK2)",Philips,NTSC,
+         16*160.00,16*454.00,0xa0,0x90,0x30,0x8e,732},
+        { "Temic PAL_I (4066 FY5)", TEMIC, PAL_I,
+          16*169.00, 16*454.00, 0xa0,0x90,0x30,0x8e,623},
+        { "Temic PAL* auto (4006 FN5)", TEMIC, PAL,
+          16*169.00, 16*454.00, 0xa0,0x90,0x30,0x8e,623},
+
+        { "Temic PAL_BG (4009 FR5) or PAL_I (4069 FR5)", TEMIC, PAL,
+          16*141.00, 16*464.00, 0xa0,0x90,0x30,0x8e,623},
+        { "Temic NTSC (4039 FR5)", TEMIC, NTSC,
+          16*158.00, 16*453.00, 0xa0,0x90,0x30,0x8e,732},
+        { "Temic PAL/SECAM multi (4046 FM5)", TEMIC, PAL,
+          16*169.00, 16*454.00, 0xa0,0x90,0x30,0x8e,623},
+        { "Philips PAL_DK (FI1256 and compatibles)", Philips, PAL,
+         16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
+
+       { "Philips PAL/SECAM multi (FQ1216ME)", Philips, PAL,
+         16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
+       { "LG PAL_I+FM (TAPC-I001D)", LGINNOTEK, PAL_I,
+         16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
+       { "LG PAL_I (TAPC-I701D)", LGINNOTEK, PAL_I,
+         16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
+       { "LG NTSC+FM (TPI8NSR01F)", LGINNOTEK, NTSC,
+         16*210.00,16*497.00,0xa0,0x90,0x30,0x8e,732},
+
+       { "LG PAL_BG+FM (TPI8PSB01D)", LGINNOTEK, PAL,
+         16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
+       { "LG PAL_BG (TPI8PSB11D)", LGINNOTEK, PAL,
+         16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623},
+       { "Temic PAL* auto + FM (4009 FN5)", TEMIC, PAL,
+         16*141.00, 16*464.00, 0xa0,0x90,0x30,0x8e,623},
+       { "SHARP NTSC_JP (2U5JF5540)", SHARP, NTSC, /* 940=16*58.75 NTSC@Japan */
+         16*137.25,16*317.25,0x01,0x02,0x08,0x8e,940 },
+
+       { "Samsung PAL TCPM9091PD27", Samsung, PAL,  /* from sourceforge v3tv */
+          16*169,16*464,0xA0,0x90,0x30,0x8e,623},
+       { "MT20xx universal", Microtune,PAL|NTSC,
+         /* see mt20xx.c for details */ },
+       { "Temic PAL_BG (4106 FH5)", TEMIC, PAL,
+          16*141.00, 16*464.00, 0xa0,0x90,0x30,0x8e,623},
+       { "Temic PAL_DK/SECAM_L (4012 FY5)", TEMIC, PAL,
+          16*140.25, 16*463.25, 0x02,0x04,0x01,0x8e,623},
+
+       { "Temic NTSC (4136 FY5)", TEMIC, NTSC,
+          16*158.00, 16*453.00, 0xa0,0x90,0x30,0x8e,732},
+        { "LG PAL (newer TAPC series)", LGINNOTEK, PAL,
+          16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,623},
+       { "Philips PAL/SECAM multi (FM1216ME MK3)", Philips, PAL,
+         16*160.00,16*442.00,0x01,0x02,0x04,0x8e,623 },
+       { "LG NTSC (newer TAPC series)", LGINNOTEK, NTSC,
+          16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,732},
+
+       { "HITACHI V7-J180AT", HITACHI, NTSC,
+         16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,940 },
+       { "Philips PAL_MK (FI1216 MK)", Philips, PAL,
+         16*140.25,16*463.25,0x01,0xc2,0xcf,0x8e,623},
+       { "Philips 1236D ATSC/NTSC daul in",Philips,ATSC,
+         16*157.25,16*454.00,0xa0,0x90,0x30,0x8e,732},
+        { "Philips NTSC MK3 (FM1236MK3 or FM1236/F)", Philips, NTSC,
+          16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732},
+
+        { "Philips 4 in 1 (ATI TV Wonder Pro/Conexant)", Philips, NTSC,
+          16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732},
+       { "Microtune 4049 FM5",Microtune,PAL,
+         16*141.00,16*464.00,0xa0,0x90,0x30,0x8e,623},
+       { "Panasonic VP27s/ENGE4324D", Panasonic, NTSC,
+         16*160.00,16*454.00,0x01,0x02,0x08,0xce,940},
+        { "LG NTSC (TAPE series)", LGINNOTEK, NTSC,
+          16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732 },
+
+        { "Tenna TNF 8831 BGFF)", Philips, PAL,
+          16*161.25,16*463.25,0xa0,0x90,0x30,0x8e,623},
+       { "Microtune 4042 FI5 ATSC/NTSC dual in", Microtune, NTSC,
+         16*162.00,16*457.00,0xa2,0x94,0x31,0x8e,732},
+        { "TCL 2002N", TCL, NTSC,
+          16*172.00,16*448.00,0x01,0x02,0x08,0x8e,732},
+       { "Philips PAL/SECAM_D (FM 1256 I-H3)", Philips, PAL,
+         16*160.00,16*442.00,0x01,0x02,0x04,0x8e,623 },
+
+       { "Thomson DDT 7610 (ATSC/NTSC)", THOMSON, ATSC,
+         16*157.25,16*454.00,0x39,0x3a,0x3c,0x8e,732},
+       { "Philips FQ1286", Philips, NTSC,
+         16*160.00,16*454.00,0x41,0x42,0x04,0x8e,940}, // UHF band untested
+       { "tda8290+75", Philips,PAL|NTSC,
+         /* see tda8290.c for details */ },
+       { "LG PAL (TAPE series)", LGINNOTEK, PAL,
+          16*170.00, 16*450.00, 0x01,0x02,0x08,0xce,623},
+
+        { "Philips PAL/SECAM multi (FQ1216AME MK4)", Philips, PAL,
+          16*160.00,16*442.00,0x01,0x02,0x04,0xce,623 },
+        { "Philips FQ1236A MK4", Philips, NTSC,
+          16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732 },
+
+};
+unsigned const int tuner_count = ARRAY_SIZE(tuners);
+
+/* ---------------------------------------------------------------------- */
+
+static int tuner_getstatus(struct i2c_client *c)
+{
+       unsigned char byte;
+
+       if (1 != i2c_master_recv(c,&byte,1))
+               return 0;
+       return byte;
+}
+
+#define TUNER_POR       0x80
+#define TUNER_FL        0x40
+#define TUNER_MODE      0x38
+#define TUNER_AFC       0x07
+
+#define TUNER_STEREO    0x10 /* radio mode */
+#define TUNER_SIGNAL    0x07 /* radio mode */
+
+static int tuner_signal(struct i2c_client *c)
+{
+       return (tuner_getstatus(c) & TUNER_SIGNAL)<<13;
+}
+
+static int tuner_stereo(struct i2c_client *c)
+{
+       return (tuner_getstatus (c) & TUNER_STEREO);
+}
+
+#if 0 /* unused */
+static int tuner_islocked (struct i2c_client *c)
+{
+        return (tuner_getstatus (c) & TUNER_FL);
+}
+
+static int tuner_afcstatus (struct i2c_client *c)
+{
+        return (tuner_getstatus (c) & TUNER_AFC) - 2;
+}
+
+static int tuner_mode (struct i2c_client *c)
+{
+        return (tuner_getstatus (c) & TUNER_MODE) >> 3;
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+static void default_set_tv_freq(struct i2c_client *c, unsigned int freq)
+{
+       struct tuner *t = i2c_get_clientdata(c);
+       u8 config;
+       u16 div;
+       struct tunertype *tun;
+        unsigned char buffer[4];
+       int rc;
+
+       tun = &tuners[t->type];
+       if (freq < tun->thresh1) {
+               config = tun->VHF_L;
+               tuner_dbg("tv: VHF lowrange\n");
+       } else if (freq < tun->thresh2) {
+               config = tun->VHF_H;
+               tuner_dbg("tv: VHF high range\n");
+       } else {
+               config = tun->UHF;
+               tuner_dbg("tv: UHF range\n");
+       }
+
+
+       /* tv norm specific stuff for multi-norm tuners */
+       switch (t->type) {
+       case TUNER_PHILIPS_SECAM: // FI1216MF
+               /* 0x01 -> ??? no change ??? */
+               /* 0x02 -> PAL BDGHI / SECAM L */
+               /* 0x04 -> ??? PAL others / SECAM others ??? */
+               config &= ~0x02;
+               if (t->std & V4L2_STD_SECAM)
+                       config |= 0x02;
+               break;
+
+       case TUNER_TEMIC_4046FM5:
+               config &= ~0x0f;
+
+               if (t->std & V4L2_STD_PAL_BG) {
+                       config |= TEMIC_SET_PAL_BG;
+
+               } else if (t->std & V4L2_STD_PAL_I) {
+                       config |= TEMIC_SET_PAL_I;
+
+               } else if (t->std & V4L2_STD_PAL_DK) {
+                       config |= TEMIC_SET_PAL_DK;
+
+               } else if (t->std & V4L2_STD_SECAM_L) {
+                       config |= TEMIC_SET_PAL_L;
+
+               }
+               break;
+
+       case TUNER_PHILIPS_FQ1216ME:
+               config &= ~0x0f;
+
+               if (t->std & (V4L2_STD_PAL_BG|V4L2_STD_PAL_DK)) {
+                       config |= PHILIPS_SET_PAL_BGDK;
+
+               } else if (t->std & V4L2_STD_PAL_I) {
+                       config |= PHILIPS_SET_PAL_I;
+
+               } else if (t->std & V4L2_STD_SECAM_L) {
+                       config |= PHILIPS_SET_PAL_L;
+
+               }
+               break;
+
+       case TUNER_PHILIPS_ATSC:
+               /* 0x00 -> ATSC antenna input 1 */
+               /* 0x01 -> ATSC antenna input 2 */
+               /* 0x02 -> NTSC antenna input 1 */
+               /* 0x03 -> NTSC antenna input 2 */
+               config &= ~0x03;
+               if (!(t->std & V4L2_STD_ATSC))
+                       config |= 2;
+               /* FIXME: input */
+               break;
+
+       case TUNER_MICROTUNE_4042FI5:
+               /* Set the charge pump for fast tuning */
+               tun->config |= 0x40;
+               break;
+       }
+
+       /*
+        * Philips FI1216MK2 remark from specification :
+        * for channel selection involving band switching, and to ensure
+        * smooth tuning to the desired channel without causing
+        * unnecessary charge pump action, it is recommended to consider
+        * the difference between wanted channel frequency and the
+        * current channel frequency.  Unnecessary charge pump action
+        * will result in very low tuning voltage which may drive the
+        * oscillator to extreme conditions.
+        *
+        * Progfou: specification says to send config data before
+        * frequency in case (wanted frequency < current frequency).
+        */
+
+       div=freq + tun->IFPCoff;
+       if (t->type == TUNER_PHILIPS_SECAM && freq < t->freq) {
+               buffer[0] = tun->config;
+               buffer[1] = config;
+               buffer[2] = (div>>8) & 0x7f;
+               buffer[3] = div      & 0xff;
+       } else {
+               buffer[0] = (div>>8) & 0x7f;
+               buffer[1] = div      & 0xff;
+               buffer[2] = tun->config;
+               buffer[3] = config;
+       }
+       tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n",
+                 buffer[0],buffer[1],buffer[2],buffer[3]);
+
+        if (4 != (rc = i2c_master_send(c,buffer,4)))
+               tuner_warn("i2c i/o error: rc == %d (should be 4)\n",rc);
+
+       if (t->type == TUNER_MICROTUNE_4042FI5) {
+               // FIXME - this may also work for other tuners
+               unsigned long timeout = jiffies + msecs_to_jiffies(1);
+               u8 status_byte = 0;
+
+               /* Wait until the PLL locks */
+               for (;;) {
+                       if (time_after(jiffies,timeout))
+                               return;
+                       if (1 != (rc = i2c_master_recv(c,&status_byte,1))) {
+                               tuner_warn("i2c i/o read error: rc == %d (should be 1)\n",rc);
+                               break;
+                       }
+                       /* bit 6 is PLL locked indicator */
+                       if (status_byte & 0x40)
+                               break;
+                       udelay(10);
+               }
+
+               /* Set the charge pump for optimized phase noise figure */
+               tun->config &= ~0x40;
+               buffer[0] = (div>>8) & 0x7f;
+               buffer[1] = div      & 0xff;
+               buffer[2] = tun->config;
+               buffer[3] = config;
+               tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n",
+                      buffer[0],buffer[1],buffer[2],buffer[3]);
+
+               if (4 != (rc = i2c_master_send(c,buffer,4)))
+                       tuner_warn("i2c i/o error: rc == %d (should be 4)\n",rc);
+       }
+}
+
+static void default_set_radio_freq(struct i2c_client *c, unsigned int freq)
+{
+       struct tunertype *tun;
+       struct tuner *t = i2c_get_clientdata(c);
+        unsigned char buffer[4];
+       unsigned div;
+       int rc;
+
+       tun=&tuners[t->type];
+       div = freq + (int)(16*10.7);
+       buffer[2] = tun->config;
+
+       switch (t->type) {
+       case TUNER_PHILIPS_FM1216ME_MK3:
+       case TUNER_PHILIPS_FM1236_MK3:
+               buffer[3] = 0x19;
+               break;
+       case TUNER_PHILIPS_FM1256_IH3:
+               div = (20 * freq)/16 + 333 * 2;
+               buffer[2] = 0x80;
+               buffer[3] = 0x19;
+               break;
+       case TUNER_LG_PAL_FM:
+               buffer[3] = 0xa5;
+               break;
+       default:
+               buffer[3] = 0xa4;
+               break;
+       }
+        buffer[0] = (div>>8) & 0x7f;
+        buffer[1] = div      & 0xff;
+
+       tuner_dbg("radio 0x%02x 0x%02x 0x%02x 0x%02x\n",
+              buffer[0],buffer[1],buffer[2],buffer[3]);
+
+        if (4 != (rc = i2c_master_send(c,buffer,4)))
+               tuner_warn("i2c i/o error: rc == %d (should be 4)\n",rc);
+}
+
+int default_tuner_init(struct i2c_client *c)
+{
+       struct tuner *t = i2c_get_clientdata(c);
+
+       tuner_info("type set to %d (%s)\n",
+                  t->type, tuners[t->type].name);
+       strlcpy(c->name, tuners[t->type].name, sizeof(c->name));
+
+       t->tv_freq    = default_set_tv_freq;
+       t->radio_freq = default_set_radio_freq;
+       t->has_signal = tuner_signal;
+       t->is_stereo  = tuner_stereo;
+       return 0;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
new file mode 100644 (file)
index 0000000..8acc655
--- /dev/null
@@ -0,0 +1,5530 @@
+/* bnx2.c: Broadcom NX2 network driver.
+ *
+ * Copyright (c) 2004, 2005 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.
+ *
+ * Written by: Michael Chan  (mchan@broadcom.com)
+ */
+
+#include "bnx2.h"
+#include "bnx2_fw.h"
+
+#define DRV_MODULE_NAME                "bnx2"
+#define PFX DRV_MODULE_NAME    ": "
+#define DRV_MODULE_VERSION     "1.2.19"
+#define DRV_MODULE_RELDATE     "May 23, 2005"
+
+#define RUN_AT(x) (jiffies + (x))
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (5*HZ)
+
+static char version[] __devinitdata =
+       "Broadcom NetXtreme II Gigabit Ethernet Driver " DRV_MODULE_NAME " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
+
+MODULE_AUTHOR("Michael Chan <mchan@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706 Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+static int disable_msi = 0;
+
+module_param(disable_msi, int, 0);
+MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");
+
+typedef enum {
+       BCM5706 = 0,
+       NC370T,
+       NC370I,
+       BCM5706S,
+       NC370F,
+} board_t;
+
+/* indexed by board_t, above */
+static struct {
+       char *name;
+} board_info[] __devinitdata = {
+       { "Broadcom NetXtreme II BCM5706 1000Base-T" },
+       { "HP NC370T Multifunction Gigabit Server Adapter" },
+       { "HP NC370i Multifunction Gigabit Server Adapter" },
+       { "Broadcom NetXtreme II BCM5706 1000Base-SX" },
+       { "HP NC370F Multifunction Gigabit Server Adapter" },
+       { 0 },
+       };
+
+static struct pci_device_id bnx2_pci_tbl[] = {
+       { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706,
+         PCI_VENDOR_ID_HP, 0x3101, 0, 0, NC370T },
+       { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706,
+         PCI_VENDOR_ID_HP, 0x3106, 0, 0, NC370I },
+       { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5706 },
+       { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S,
+         PCI_VENDOR_ID_HP, 0x3102, 0, 0, NC370F },
+       { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5706S },
+       { 0, }
+};
+
+static struct flash_spec flash_table[] =
+{
+       /* Slow EEPROM */
+       {0x00000000, 0x40030380, 0x009f0081, 0xa184a053, 0xaf000400,
+        1, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE,
+        SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE,
+        "EEPROM - slow"},
+       /* Fast EEPROM */
+       {0x02000000, 0x62008380, 0x009f0081, 0xa184a053, 0xaf000400,
+        1, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE,
+        SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE,
+        "EEPROM - fast"},
+       /* ATMEL AT45DB011B (buffered flash) */
+       {0x02000003, 0x6e008173, 0x00570081, 0x68848353, 0xaf000400,
+        1, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE,
+        BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE,
+        "Buffered flash"},
+       /* Saifun SA25F005 (non-buffered flash) */
+               /* strap, cfg1, & write1 need updates */
+       {0x01000003, 0x5f008081, 0x00050081, 0x03840253, 0xaf020406,
+        0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
+        SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE,
+        "Non-buffered flash (64kB)"},
+       /* Saifun SA25F010 (non-buffered flash) */
+       /* strap, cfg1, & write1 need updates */
+       {0x00000001, 0x47008081, 0x00050081, 0x03840253, 0xaf020406,
+        0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
+        SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*2,
+        "Non-buffered flash (128kB)"},
+       /* Saifun SA25F020 (non-buffered flash) */
+       /* strap, cfg1, & write1 need updates */
+       {0x00000003, 0x4f008081, 0x00050081, 0x03840253, 0xaf020406,
+        0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
+        SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*4,
+        "Non-buffered flash (256kB)"},
+};
+
+MODULE_DEVICE_TABLE(pci, bnx2_pci_tbl);
+
+static u32
+bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset)
+{
+       REG_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset);
+       return (REG_RD(bp, BNX2_PCICFG_REG_WINDOW));
+}
+
+static void
+bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val)
+{
+       REG_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset);
+       REG_WR(bp, BNX2_PCICFG_REG_WINDOW, val);
+}
+
+static void
+bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val)
+{
+       offset += cid_addr;
+       REG_WR(bp, BNX2_CTX_DATA_ADR, offset);
+       REG_WR(bp, BNX2_CTX_DATA, val);
+}
+
+static int
+bnx2_read_phy(struct bnx2 *bp, u32 reg, u32 *val)
+{
+       u32 val1;
+       int i, ret;
+
+       if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+               val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+               val1 &= ~BNX2_EMAC_MDIO_MODE_AUTO_POLL;
+
+               REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
+               REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+
+               udelay(40);
+       }
+
+       val1 = (bp->phy_addr << 21) | (reg << 16) |
+               BNX2_EMAC_MDIO_COMM_COMMAND_READ | BNX2_EMAC_MDIO_COMM_DISEXT |
+               BNX2_EMAC_MDIO_COMM_START_BUSY;
+       REG_WR(bp, BNX2_EMAC_MDIO_COMM, val1);
+
+       for (i = 0; i < 50; i++) {
+               udelay(10);
+
+               val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM);
+               if (!(val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)) {
+                       udelay(5);
+
+                       val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM);
+                       val1 &= BNX2_EMAC_MDIO_COMM_DATA;
+
+                       break;
+               }
+       }
+
+       if (val1 & BNX2_EMAC_MDIO_COMM_START_BUSY) {
+               *val = 0x0;
+               ret = -EBUSY;
+       }
+       else {
+               *val = val1;
+               ret = 0;
+       }
+
+       if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+               val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+               val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL;
+
+               REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
+               REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+
+               udelay(40);
+       }
+
+       return ret;
+}
+
+static int
+bnx2_write_phy(struct bnx2 *bp, u32 reg, u32 val)
+{
+       u32 val1;
+       int i, ret;
+
+       if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+               val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+               val1 &= ~BNX2_EMAC_MDIO_MODE_AUTO_POLL;
+
+               REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
+               REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+
+               udelay(40);
+       }
+
+       val1 = (bp->phy_addr << 21) | (reg << 16) | val |
+               BNX2_EMAC_MDIO_COMM_COMMAND_WRITE |
+               BNX2_EMAC_MDIO_COMM_START_BUSY | BNX2_EMAC_MDIO_COMM_DISEXT;
+       REG_WR(bp, BNX2_EMAC_MDIO_COMM, val1);
+    
+       for (i = 0; i < 50; i++) {
+               udelay(10);
+
+               val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM);
+               if (!(val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)) {
+                       udelay(5);
+                       break;
+               }
+       }
+
+       if (val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)
+               ret = -EBUSY;
+       else
+               ret = 0;
+
+       if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+               val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+               val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL;
+
+               REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
+               REG_RD(bp, BNX2_EMAC_MDIO_MODE);
+
+               udelay(40);
+       }
+
+       return ret;
+}
+
+static void
+bnx2_disable_int(struct bnx2 *bp)
+{
+       REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
+              BNX2_PCICFG_INT_ACK_CMD_MASK_INT);
+       REG_RD(bp, BNX2_PCICFG_INT_ACK_CMD);
+}
+
+static void
+bnx2_enable_int(struct bnx2 *bp)
+{
+       u32 val;
+
+       REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
+              BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | bp->last_status_idx);
+
+       val = REG_RD(bp, BNX2_HC_COMMAND);
+       REG_WR(bp, BNX2_HC_COMMAND, val | BNX2_HC_COMMAND_COAL_NOW);
+}
+
+static void
+bnx2_disable_int_sync(struct bnx2 *bp)
+{
+       atomic_inc(&bp->intr_sem);
+       bnx2_disable_int(bp);
+       synchronize_irq(bp->pdev->irq);
+}
+
+static void
+bnx2_netif_stop(struct bnx2 *bp)
+{
+       bnx2_disable_int_sync(bp);
+       if (netif_running(bp->dev)) {
+               netif_poll_disable(bp->dev);
+               netif_tx_disable(bp->dev);
+               bp->dev->trans_start = jiffies; /* prevent tx timeout */
+       }
+}
+
+static void
+bnx2_netif_start(struct bnx2 *bp)
+{
+       if (atomic_dec_and_test(&bp->intr_sem)) {
+               if (netif_running(bp->dev)) {
+                       netif_wake_queue(bp->dev);
+                       netif_poll_enable(bp->dev);
+                       bnx2_enable_int(bp);
+               }
+       }
+}
+
+static void
+bnx2_free_mem(struct bnx2 *bp)
+{
+       if (bp->stats_blk) {
+               pci_free_consistent(bp->pdev, sizeof(struct statistics_block),
+                                   bp->stats_blk, bp->stats_blk_mapping);
+               bp->stats_blk = NULL;
+       }
+       if (bp->status_blk) {
+               pci_free_consistent(bp->pdev, sizeof(struct status_block),
+                                   bp->status_blk, bp->status_blk_mapping);
+               bp->status_blk = NULL;
+       }
+       if (bp->tx_desc_ring) {
+               pci_free_consistent(bp->pdev,
+                                   sizeof(struct tx_bd) * TX_DESC_CNT,
+                                   bp->tx_desc_ring, bp->tx_desc_mapping);
+               bp->tx_desc_ring = NULL;
+       }
+       if (bp->tx_buf_ring) {
+               kfree(bp->tx_buf_ring);
+               bp->tx_buf_ring = NULL;
+       }
+       if (bp->rx_desc_ring) {
+               pci_free_consistent(bp->pdev,
+                                   sizeof(struct rx_bd) * RX_DESC_CNT,
+                                   bp->rx_desc_ring, bp->rx_desc_mapping);
+               bp->rx_desc_ring = NULL;
+       }
+       if (bp->rx_buf_ring) {
+               kfree(bp->rx_buf_ring);
+               bp->rx_buf_ring = NULL;
+       }
+}
+
+static int
+bnx2_alloc_mem(struct bnx2 *bp)
+{
+       bp->tx_buf_ring = kmalloc(sizeof(struct sw_bd) * TX_DESC_CNT,
+                                    GFP_KERNEL);
+       if (bp->tx_buf_ring == NULL)
+               return -ENOMEM;
+
+       memset(bp->tx_buf_ring, 0, sizeof(struct sw_bd) * TX_DESC_CNT);
+       bp->tx_desc_ring = pci_alloc_consistent(bp->pdev,
+                                               sizeof(struct tx_bd) *
+                                               TX_DESC_CNT,
+                                               &bp->tx_desc_mapping);
+       if (bp->tx_desc_ring == NULL)
+               goto alloc_mem_err;
+
+       bp->rx_buf_ring = kmalloc(sizeof(struct sw_bd) * RX_DESC_CNT,
+                                    GFP_KERNEL);
+       if (bp->rx_buf_ring == NULL)
+               goto alloc_mem_err;
+
+       memset(bp->rx_buf_ring, 0, sizeof(struct sw_bd) * RX_DESC_CNT);
+       bp->rx_desc_ring = pci_alloc_consistent(bp->pdev,
+                                               sizeof(struct rx_bd) *
+                                               RX_DESC_CNT,
+                                               &bp->rx_desc_mapping);
+       if (bp->rx_desc_ring == NULL)
+               goto alloc_mem_err;
+
+       bp->status_blk = pci_alloc_consistent(bp->pdev,
+                                             sizeof(struct status_block),
+                                             &bp->status_blk_mapping);
+       if (bp->status_blk == NULL)
+               goto alloc_mem_err;
+
+       memset(bp->status_blk, 0, sizeof(struct status_block));
+
+       bp->stats_blk = pci_alloc_consistent(bp->pdev,
+                                            sizeof(struct statistics_block),
+                                            &bp->stats_blk_mapping);
+       if (bp->stats_blk == NULL)
+               goto alloc_mem_err;
+
+       memset(bp->stats_blk, 0, sizeof(struct statistics_block));
+
+       return 0;
+
+alloc_mem_err:
+       bnx2_free_mem(bp);
+       return -ENOMEM;
+}
+
+static void
+bnx2_report_link(struct bnx2 *bp)
+{
+       if (bp->link_up) {
+               netif_carrier_on(bp->dev);
+               printk(KERN_INFO PFX "%s NIC Link is Up, ", bp->dev->name);
+
+               printk("%d Mbps ", bp->line_speed);
+
+               if (bp->duplex == DUPLEX_FULL)
+                       printk("full duplex");
+               else
+                       printk("half duplex");
+
+               if (bp->flow_ctrl) {
+                       if (bp->flow_ctrl & FLOW_CTRL_RX) {
+                               printk(", receive ");
+                               if (bp->flow_ctrl & FLOW_CTRL_TX)
+                                       printk("& transmit ");
+                       }
+                       else {
+                               printk(", transmit ");
+                       }
+                       printk("flow control ON");
+               }
+               printk("\n");
+       }
+       else {
+               netif_carrier_off(bp->dev);
+               printk(KERN_ERR PFX "%s NIC Link is Down\n", bp->dev->name);
+       }
+}
+
+static void
+bnx2_resolve_flow_ctrl(struct bnx2 *bp)
+{
+       u32 local_adv, remote_adv;
+
+       bp->flow_ctrl = 0;
+       if ((bp->autoneg & (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) != 
+               (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) {
+
+               if (bp->duplex == DUPLEX_FULL) {
+                       bp->flow_ctrl = bp->req_flow_ctrl;
+               }
+               return;
+       }
+
+       if (bp->duplex != DUPLEX_FULL) {
+               return;
+       }
+
+       bnx2_read_phy(bp, MII_ADVERTISE, &local_adv);
+       bnx2_read_phy(bp, MII_LPA, &remote_adv);
+
+       if (bp->phy_flags & PHY_SERDES_FLAG) {
+               u32 new_local_adv = 0;
+               u32 new_remote_adv = 0;
+
+               if (local_adv & ADVERTISE_1000XPAUSE)
+                       new_local_adv |= ADVERTISE_PAUSE_CAP;
+               if (local_adv & ADVERTISE_1000XPSE_ASYM)
+                       new_local_adv |= ADVERTISE_PAUSE_ASYM;
+               if (remote_adv & ADVERTISE_1000XPAUSE)
+                       new_remote_adv |= ADVERTISE_PAUSE_CAP;
+               if (remote_adv & ADVERTISE_1000XPSE_ASYM)
+                       new_remote_adv |= ADVERTISE_PAUSE_ASYM;
+
+               local_adv = new_local_adv;
+               remote_adv = new_remote_adv;
+       }
+
+       /* See Table 28B-3 of 802.3ab-1999 spec. */
+       if (local_adv & ADVERTISE_PAUSE_CAP) {
+               if(local_adv & ADVERTISE_PAUSE_ASYM) {
+                       if (remote_adv & ADVERTISE_PAUSE_CAP) {
+                               bp->flow_ctrl = FLOW_CTRL_TX | FLOW_CTRL_RX;
+                       }
+                       else if (remote_adv & ADVERTISE_PAUSE_ASYM) {
+                               bp->flow_ctrl = FLOW_CTRL_RX;
+                       }
+               }
+               else {
+                       if (remote_adv & ADVERTISE_PAUSE_CAP) {
+                               bp->flow_ctrl = FLOW_CTRL_TX | FLOW_CTRL_RX;
+                       }
+               }
+       }
+       else if (local_adv & ADVERTISE_PAUSE_ASYM) {
+               if ((remote_adv & ADVERTISE_PAUSE_CAP) &&
+                       (remote_adv & ADVERTISE_PAUSE_ASYM)) {
+
+                       bp->flow_ctrl = FLOW_CTRL_TX;
+               }
+       }
+}
+
+static int
+bnx2_serdes_linkup(struct bnx2 *bp)
+{
+       u32 bmcr, local_adv, remote_adv, common;
+
+       bp->link_up = 1;
+       bp->line_speed = SPEED_1000;
+
+       bnx2_read_phy(bp, MII_BMCR, &bmcr);
+       if (bmcr & BMCR_FULLDPLX) {
+               bp->duplex = DUPLEX_FULL;
+       }
+       else {
+               bp->duplex = DUPLEX_HALF;
+       }
+
+       if (!(bmcr & BMCR_ANENABLE)) {
+               return 0;
+       }
+
+       bnx2_read_phy(bp, MII_ADVERTISE, &local_adv);
+       bnx2_read_phy(bp, MII_LPA, &remote_adv);
+
+       common = local_adv & remote_adv;
+       if (common & (ADVERTISE_1000XHALF | ADVERTISE_1000XFULL)) {
+
+               if (common & ADVERTISE_1000XFULL) {
+                       bp->duplex = DUPLEX_FULL;
+               }
+               else {
+                       bp->duplex = DUPLEX_HALF;
+               }
+       }
+
+       return 0;
+}
+
+static int
+bnx2_copper_linkup(struct bnx2 *bp)
+{
+       u32 bmcr;
+
+       bnx2_read_phy(bp, MII_BMCR, &bmcr);
+       if (bmcr & BMCR_ANENABLE) {
+               u32 local_adv, remote_adv, common;
+
+               bnx2_read_phy(bp, MII_CTRL1000, &local_adv);
+               bnx2_read_phy(bp, MII_STAT1000, &remote_adv);
+
+               common = local_adv & (remote_adv >> 2);
+               if (common & ADVERTISE_1000FULL) {
+                       bp->line_speed = SPEED_1000;
+                       bp->duplex = DUPLEX_FULL;
+               }
+               else if (common & ADVERTISE_1000HALF) {
+                       bp->line_speed = SPEED_1000;
+                       bp->duplex = DUPLEX_HALF;
+               }
+               else {
+                       bnx2_read_phy(bp, MII_ADVERTISE, &local_adv);
+                       bnx2_read_phy(bp, MII_LPA, &remote_adv);
+
+                       common = local_adv & remote_adv;
+                       if (common & ADVERTISE_100FULL) {
+                               bp->line_speed = SPEED_100;
+                               bp->duplex = DUPLEX_FULL;
+                       }
+                       else if (common & ADVERTISE_100HALF) {
+                               bp->line_speed = SPEED_100;
+                               bp->duplex = DUPLEX_HALF;
+                       }
+                       else if (common & ADVERTISE_10FULL) {
+                               bp->line_speed = SPEED_10;
+                               bp->duplex = DUPLEX_FULL;
+                       }
+                       else if (common & ADVERTISE_10HALF) {
+                               bp->line_speed = SPEED_10;
+                               bp->duplex = DUPLEX_HALF;
+                       }
+                       else {
+                               bp->line_speed = 0;
+                               bp->link_up = 0;
+                       }
+               }
+       }
+       else {
+               if (bmcr & BMCR_SPEED100) {
+                       bp->line_speed = SPEED_100;
+               }
+               else {
+                       bp->line_speed = SPEED_10;
+               }
+               if (bmcr & BMCR_FULLDPLX) {
+                       bp->duplex = DUPLEX_FULL;
+               }
+               else {
+                       bp->duplex = DUPLEX_HALF;
+               }
+       }
+
+       return 0;
+}
+
+static int
+bnx2_set_mac_link(struct bnx2 *bp)
+{
+       u32 val;
+
+       REG_WR(bp, BNX2_EMAC_TX_LENGTHS, 0x2620);
+       if (bp->link_up && (bp->line_speed == SPEED_1000) &&
+               (bp->duplex == DUPLEX_HALF)) {
+               REG_WR(bp, BNX2_EMAC_TX_LENGTHS, 0x26ff);
+       }
+
+       /* Configure the EMAC mode register. */
+       val = REG_RD(bp, BNX2_EMAC_MODE);
+
+       val &= ~(BNX2_EMAC_MODE_PORT | BNX2_EMAC_MODE_HALF_DUPLEX |
+               BNX2_EMAC_MODE_MAC_LOOP | BNX2_EMAC_MODE_FORCE_LINK);
+
+       if (bp->link_up) {
+               if (bp->line_speed != SPEED_1000)
+                       val |= BNX2_EMAC_MODE_PORT_MII;
+               else
+                       val |= BNX2_EMAC_MODE_PORT_GMII;
+       }
+       else {
+               val |= BNX2_EMAC_MODE_PORT_GMII;
+       }
+
+       /* Set the MAC to operate in the appropriate duplex mode. */
+       if (bp->duplex == DUPLEX_HALF)
+               val |= BNX2_EMAC_MODE_HALF_DUPLEX;
+       REG_WR(bp, BNX2_EMAC_MODE, val);
+
+       /* Enable/disable rx PAUSE. */
+       bp->rx_mode &= ~BNX2_EMAC_RX_MODE_FLOW_EN;
+
+       if (bp->flow_ctrl & FLOW_CTRL_RX)
+               bp->rx_mode |= BNX2_EMAC_RX_MODE_FLOW_EN;
+       REG_WR(bp, BNX2_EMAC_RX_MODE, bp->rx_mode);
+
+       /* Enable/disable tx PAUSE. */
+       val = REG_RD(bp, BNX2_EMAC_TX_MODE);
+       val &= ~BNX2_EMAC_TX_MODE_FLOW_EN;
+
+       if (bp->flow_ctrl & FLOW_CTRL_TX)
+               val |= BNX2_EMAC_TX_MODE_FLOW_EN;
+       REG_WR(bp, BNX2_EMAC_TX_MODE, val);
+
+       /* Acknowledge the interrupt. */
+       REG_WR(bp, BNX2_EMAC_STATUS, BNX2_EMAC_STATUS_LINK_CHANGE);
+
+       return 0;
+}
+
+static int
+bnx2_set_link(struct bnx2 *bp)
+{
+       u32 bmsr;
+       u8 link_up;
+
+       if (bp->loopback == MAC_LOOPBACK) {
+               bp->link_up = 1;
+               return 0;
+       }
+
+       link_up = bp->link_up;
+
+       bnx2_read_phy(bp, MII_BMSR, &bmsr);
+       bnx2_read_phy(bp, MII_BMSR, &bmsr);
+
+       if ((bp->phy_flags & PHY_SERDES_FLAG) &&
+           (CHIP_NUM(bp) == CHIP_NUM_5706)) {
+               u32 val;
+
+               val = REG_RD(bp, BNX2_EMAC_STATUS);
+               if (val & BNX2_EMAC_STATUS_LINK)
+                       bmsr |= BMSR_LSTATUS;
+               else
+                       bmsr &= ~BMSR_LSTATUS;
+       }
+
+       if (bmsr & BMSR_LSTATUS) {
+               bp->link_up = 1;
+
+               if (bp->phy_flags & PHY_SERDES_FLAG) {
+                       bnx2_serdes_linkup(bp);
+               }
+               else {
+                       bnx2_copper_linkup(bp);
+               }
+               bnx2_resolve_flow_ctrl(bp);
+       }
+       else {
+               if ((bp->phy_flags & PHY_SERDES_FLAG) &&
+                       (bp->autoneg & AUTONEG_SPEED)) {
+
+                       u32 bmcr;
+
+                       bnx2_read_phy(bp, MII_BMCR, &bmcr);
+                       if (!(bmcr & BMCR_ANENABLE)) {
+                               bnx2_write_phy(bp, MII_BMCR, bmcr |
+                                       BMCR_ANENABLE);
+                       }
+               }
+               bp->phy_flags &= ~PHY_PARALLEL_DETECT_FLAG;
+               bp->link_up = 0;
+       }
+
+       if (bp->link_up != link_up) {
+               bnx2_report_link(bp);
+       }
+
+       bnx2_set_mac_link(bp);
+
+       return 0;
+}
+
+static int
+bnx2_reset_phy(struct bnx2 *bp)
+{
+       int i;
+       u32 reg;
+
+        bnx2_write_phy(bp, MII_BMCR, BMCR_RESET);
+
+#define PHY_RESET_MAX_WAIT 100
+       for (i = 0; i < PHY_RESET_MAX_WAIT; i++) {
+               udelay(10);
+
+               bnx2_read_phy(bp, MII_BMCR, &reg);
+               if (!(reg & BMCR_RESET)) {
+                       udelay(20);
+                       break;
+               }
+       }
+       if (i == PHY_RESET_MAX_WAIT) {
+               return -EBUSY;
+       }
+       return 0;
+}
+
+static u32
+bnx2_phy_get_pause_adv(struct bnx2 *bp)
+{
+       u32 adv = 0;
+
+       if ((bp->req_flow_ctrl & (FLOW_CTRL_RX | FLOW_CTRL_TX)) ==
+               (FLOW_CTRL_RX | FLOW_CTRL_TX)) {
+
+               if (bp->phy_flags & PHY_SERDES_FLAG) {
+                       adv = ADVERTISE_1000XPAUSE;
+               }
+               else {
+                       adv = ADVERTISE_PAUSE_CAP;
+               }
+       }
+       else if (bp->req_flow_ctrl & FLOW_CTRL_TX) {
+               if (bp->phy_flags & PHY_SERDES_FLAG) {
+                       adv = ADVERTISE_1000XPSE_ASYM;
+               }
+               else {
+                       adv = ADVERTISE_PAUSE_ASYM;
+               }
+       }
+       else if (bp->req_flow_ctrl & FLOW_CTRL_RX) {
+               if (bp->phy_flags & PHY_SERDES_FLAG) {
+                       adv = ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM;
+               }
+               else {
+                       adv = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+               }
+       }
+       return adv;
+}
+
+static int
+bnx2_setup_serdes_phy(struct bnx2 *bp)
+{
+       u32 adv, bmcr;
+       u32 new_adv = 0;
+
+       if (!(bp->autoneg & AUTONEG_SPEED)) {
+               u32 new_bmcr;
+
+               bnx2_read_phy(bp, MII_BMCR, &bmcr);
+               new_bmcr = bmcr & ~BMCR_ANENABLE;
+               new_bmcr |= BMCR_SPEED1000;
+               if (bp->req_duplex == DUPLEX_FULL) {
+                       new_bmcr |= BMCR_FULLDPLX;
+               }
+               else {
+                       new_bmcr &= ~BMCR_FULLDPLX;
+               }
+               if (new_bmcr != bmcr) {
+                       /* Force a link down visible on the other side */
+                       if (bp->link_up) {
+                               bnx2_read_phy(bp, MII_ADVERTISE, &adv);
+                               adv &= ~(ADVERTISE_1000XFULL |
+                                       ADVERTISE_1000XHALF);
+                               bnx2_write_phy(bp, MII_ADVERTISE, adv);
+                               bnx2_write_phy(bp, MII_BMCR, bmcr |
+                                       BMCR_ANRESTART | BMCR_ANENABLE);
+
+                               bp->link_up = 0;
+                               netif_carrier_off(bp->dev);
+                       }
+                       bnx2_write_phy(bp, MII_BMCR, new_bmcr);
+               }
+               return 0;
+       }
+
+       if (bp->advertising & ADVERTISED_1000baseT_Full)
+               new_adv |= ADVERTISE_1000XFULL;
+
+       new_adv |= bnx2_phy_get_pause_adv(bp);
+
+       bnx2_read_phy(bp, MII_ADVERTISE, &adv);
+       bnx2_read_phy(bp, MII_BMCR, &bmcr);
+
+       bp->serdes_an_pending = 0;
+       if ((adv != new_adv) || ((bmcr & BMCR_ANENABLE) == 0)) {
+               /* Force a link down visible on the other side */
+               if (bp->link_up) {
+                       int i;
+
+                       bnx2_write_phy(bp, MII_BMCR, BMCR_LOOPBACK);
+                       for (i = 0; i < 110; i++) {
+                               udelay(100);
+                       }
+               }
+
+               bnx2_write_phy(bp, MII_ADVERTISE, new_adv);
+               bnx2_write_phy(bp, MII_BMCR, bmcr | BMCR_ANRESTART |
+                       BMCR_ANENABLE);
+               bp->serdes_an_pending = SERDES_AN_TIMEOUT / bp->timer_interval;
+       }
+
+       return 0;
+}
+
+#define ETHTOOL_ALL_FIBRE_SPEED                                                \
+       (ADVERTISED_1000baseT_Full)
+
+#define ETHTOOL_ALL_COPPER_SPEED                                       \
+       (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |            \
+       ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full |           \
+       ADVERTISED_1000baseT_Full)
+
+#define PHY_ALL_10_100_SPEED (ADVERTISE_10HALF | ADVERTISE_10FULL | \
+       ADVERTISE_100HALF | ADVERTISE_100FULL | ADVERTISE_CSMA)
+       
+#define PHY_ALL_1000_SPEED (ADVERTISE_1000HALF | ADVERTISE_1000FULL)
+
+static int
+bnx2_setup_copper_phy(struct bnx2 *bp)
+{
+       u32 bmcr;
+       u32 new_bmcr;
+
+       bnx2_read_phy(bp, MII_BMCR, &bmcr);
+
+       if (bp->autoneg & AUTONEG_SPEED) {
+               u32 adv_reg, adv1000_reg;
+               u32 new_adv_reg = 0;
+               u32 new_adv1000_reg = 0;
+
+               bnx2_read_phy(bp, MII_ADVERTISE, &adv_reg);
+               adv_reg &= (PHY_ALL_10_100_SPEED | ADVERTISE_PAUSE_CAP |
+                       ADVERTISE_PAUSE_ASYM);
+
+               bnx2_read_phy(bp, MII_CTRL1000, &adv1000_reg);
+               adv1000_reg &= PHY_ALL_1000_SPEED;
+
+               if (bp->advertising & ADVERTISED_10baseT_Half)
+                       new_adv_reg |= ADVERTISE_10HALF;
+               if (bp->advertising & ADVERTISED_10baseT_Full)
+                       new_adv_reg |= ADVERTISE_10FULL;
+               if (bp->advertising & ADVERTISED_100baseT_Half)
+                       new_adv_reg |= ADVERTISE_100HALF;
+               if (bp->advertising & ADVERTISED_100baseT_Full)
+                       new_adv_reg |= ADVERTISE_100FULL;
+               if (bp->advertising & ADVERTISED_1000baseT_Full)
+                       new_adv1000_reg |= ADVERTISE_1000FULL;
+               
+               new_adv_reg |= ADVERTISE_CSMA;
+
+               new_adv_reg |= bnx2_phy_get_pause_adv(bp);
+
+               if ((adv1000_reg != new_adv1000_reg) ||
+                       (adv_reg != new_adv_reg) ||
+                       ((bmcr & BMCR_ANENABLE) == 0)) {
+
+                       bnx2_write_phy(bp, MII_ADVERTISE, new_adv_reg);
+                       bnx2_write_phy(bp, MII_CTRL1000, new_adv1000_reg);
+                       bnx2_write_phy(bp, MII_BMCR, BMCR_ANRESTART |
+                               BMCR_ANENABLE);
+               }
+               else if (bp->link_up) {
+                       /* Flow ctrl may have changed from auto to forced */
+                       /* or vice-versa. */
+
+                       bnx2_resolve_flow_ctrl(bp);
+                       bnx2_set_mac_link(bp);
+               }
+               return 0;
+       }
+
+       new_bmcr = 0;
+       if (bp->req_line_speed == SPEED_100) {
+               new_bmcr |= BMCR_SPEED100;
+       }
+       if (bp->req_duplex == DUPLEX_FULL) {
+               new_bmcr |= BMCR_FULLDPLX;
+       }
+       if (new_bmcr != bmcr) {
+               u32 bmsr;
+               int i = 0;
+
+               bnx2_read_phy(bp, MII_BMSR, &bmsr);
+               bnx2_read_phy(bp, MII_BMSR, &bmsr);
+               
+               if (bmsr & BMSR_LSTATUS) {
+                       /* Force link down */
+                       bnx2_write_phy(bp, MII_BMCR, BMCR_LOOPBACK);
+                       do {
+                               udelay(100);
+                               bnx2_read_phy(bp, MII_BMSR, &bmsr);
+                               bnx2_read_phy(bp, MII_BMSR, &bmsr);
+                               i++;
+                       } while ((bmsr & BMSR_LSTATUS) && (i < 620));
+               }
+
+               bnx2_write_phy(bp, MII_BMCR, new_bmcr);
+
+               /* Normally, the new speed is setup after the link has
+                * gone down and up again. In some cases, link will not go
+                * down so we need to set up the new speed here.
+                */
+               if (bmsr & BMSR_LSTATUS) {
+                       bp->line_speed = bp->req_line_speed;
+                       bp->duplex = bp->req_duplex;
+                       bnx2_resolve_flow_ctrl(bp);
+                       bnx2_set_mac_link(bp);
+               }
+       }
+       return 0;
+}
+
+static int
+bnx2_setup_phy(struct bnx2 *bp)
+{
+       if (bp->loopback == MAC_LOOPBACK)
+               return 0;
+
+       if (bp->phy_flags & PHY_SERDES_FLAG) {
+               return (bnx2_setup_serdes_phy(bp));
+       }
+       else {
+               return (bnx2_setup_copper_phy(bp));
+       }
+}
+
+static int
+bnx2_init_serdes_phy(struct bnx2 *bp)
+{
+       bp->phy_flags &= ~PHY_PARALLEL_DETECT_FLAG;
+
+       if (CHIP_NUM(bp) == CHIP_NUM_5706) {
+               REG_WR(bp, BNX2_MISC_UNUSED0, 0x300);
+       }
+
+       if (bp->dev->mtu > 1500) {
+               u32 val;
+
+               /* Set extended packet length bit */
+               bnx2_write_phy(bp, 0x18, 0x7);
+               bnx2_read_phy(bp, 0x18, &val);
+               bnx2_write_phy(bp, 0x18, (val & 0xfff8) | 0x4000);
+
+               bnx2_write_phy(bp, 0x1c, 0x6c00);
+               bnx2_read_phy(bp, 0x1c, &val);
+               bnx2_write_phy(bp, 0x1c, (val & 0x3ff) | 0xec02);
+       }
+       else {
+               u32 val;
+
+               bnx2_write_phy(bp, 0x18, 0x7);
+               bnx2_read_phy(bp, 0x18, &val);
+               bnx2_write_phy(bp, 0x18, val & ~0x4007);
+
+               bnx2_write_phy(bp, 0x1c, 0x6c00);
+               bnx2_read_phy(bp, 0x1c, &val);
+               bnx2_write_phy(bp, 0x1c, (val & 0x3fd) | 0xec00);
+       }
+
+       return 0;
+}
+
+static int
+bnx2_init_copper_phy(struct bnx2 *bp)
+{
+       bp->phy_flags |= PHY_CRC_FIX_FLAG;
+
+       if (bp->phy_flags & PHY_CRC_FIX_FLAG) {
+               bnx2_write_phy(bp, 0x18, 0x0c00);
+               bnx2_write_phy(bp, 0x17, 0x000a);
+               bnx2_write_phy(bp, 0x15, 0x310b);
+               bnx2_write_phy(bp, 0x17, 0x201f);
+               bnx2_write_phy(bp, 0x15, 0x9506);
+               bnx2_write_phy(bp, 0x17, 0x401f);
+               bnx2_write_phy(bp, 0x15, 0x14e2);
+               bnx2_write_phy(bp, 0x18, 0x0400);
+       }
+
+       if (bp->dev->mtu > 1500) {
+               u32 val;
+
+               /* Set extended packet length bit */
+               bnx2_write_phy(bp, 0x18, 0x7);
+               bnx2_read_phy(bp, 0x18, &val);
+               bnx2_write_phy(bp, 0x18, val | 0x4000);
+
+               bnx2_read_phy(bp, 0x10, &val);
+               bnx2_write_phy(bp, 0x10, val | 0x1);
+       }
+       else {
+               u32 val;
+
+               bnx2_write_phy(bp, 0x18, 0x7);
+               bnx2_read_phy(bp, 0x18, &val);
+               bnx2_write_phy(bp, 0x18, val & ~0x4007);
+
+               bnx2_read_phy(bp, 0x10, &val);
+               bnx2_write_phy(bp, 0x10, val & ~0x1);
+       }
+
+       return 0;
+}
+
+
+static int
+bnx2_init_phy(struct bnx2 *bp)
+{
+       u32 val;
+       int rc = 0;
+
+       bp->phy_flags &= ~PHY_INT_MODE_MASK_FLAG;
+       bp->phy_flags |= PHY_INT_MODE_LINK_READY_FLAG;
+
+        REG_WR(bp, BNX2_EMAC_ATTENTION_ENA, BNX2_EMAC_ATTENTION_ENA_LINK);
+
+       bnx2_reset_phy(bp);
+
+       bnx2_read_phy(bp, MII_PHYSID1, &val);
+       bp->phy_id = val << 16;
+       bnx2_read_phy(bp, MII_PHYSID2, &val);
+       bp->phy_id |= val & 0xffff;
+
+       if (bp->phy_flags & PHY_SERDES_FLAG) {
+               rc = bnx2_init_serdes_phy(bp);
+       }
+       else {
+               rc = bnx2_init_copper_phy(bp);
+       }
+
+       bnx2_setup_phy(bp);
+
+       return rc;
+}
+
+static int
+bnx2_set_mac_loopback(struct bnx2 *bp)
+{
+       u32 mac_mode;
+
+       mac_mode = REG_RD(bp, BNX2_EMAC_MODE);
+       mac_mode &= ~BNX2_EMAC_MODE_PORT;
+       mac_mode |= BNX2_EMAC_MODE_MAC_LOOP | BNX2_EMAC_MODE_FORCE_LINK;
+       REG_WR(bp, BNX2_EMAC_MODE, mac_mode);
+       bp->link_up = 1;
+       return 0;
+}
+
+static int
+bnx2_fw_sync(struct bnx2 *bp, u32 msg_data)
+{
+       int i;
+       u32 val;
+
+       if (bp->fw_timed_out)
+               return -EBUSY;
+
+       bp->fw_wr_seq++;
+       msg_data |= bp->fw_wr_seq;
+
+       REG_WR_IND(bp, HOST_VIEW_SHMEM_BASE + BNX2_DRV_MB, msg_data);
+
+       /* wait for an acknowledgement. */
+       for (i = 0; i < (FW_ACK_TIME_OUT_MS * 1000)/5; i++) {
+               udelay(5);
+
+               val = REG_RD_IND(bp, HOST_VIEW_SHMEM_BASE + BNX2_FW_MB);
+
+               if ((val & BNX2_FW_MSG_ACK) == (msg_data & BNX2_DRV_MSG_SEQ))
+                       break;
+       }
+
+       /* If we timed out, inform the firmware that this is the case. */
+       if (((val & BNX2_FW_MSG_ACK) != (msg_data & BNX2_DRV_MSG_SEQ)) &&
+               ((msg_data & BNX2_DRV_MSG_DATA) != BNX2_DRV_MSG_DATA_WAIT0)) {
+
+               msg_data &= ~BNX2_DRV_MSG_CODE;
+               msg_data |= BNX2_DRV_MSG_CODE_FW_TIMEOUT;
+
+               REG_WR_IND(bp, HOST_VIEW_SHMEM_BASE + BNX2_DRV_MB, msg_data);
+
+               bp->fw_timed_out = 1;
+
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static void
+bnx2_init_context(struct bnx2 *bp)
+{
+       u32 vcid;
+
+       vcid = 96;
+       while (vcid) {
+               u32 vcid_addr, pcid_addr, offset;
+
+               vcid--;
+
+               if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
+                       u32 new_vcid;
+
+                       vcid_addr = GET_PCID_ADDR(vcid);
+                       if (vcid & 0x8) {
+                               new_vcid = 0x60 + (vcid & 0xf0) + (vcid & 0x7);
+                       }
+                       else {
+                               new_vcid = vcid;
+                       }
+                       pcid_addr = GET_PCID_ADDR(new_vcid);
+               }
+               else {
+                       vcid_addr = GET_CID_ADDR(vcid);
+                       pcid_addr = vcid_addr;
+               }
+
+               REG_WR(bp, BNX2_CTX_VIRT_ADDR, 0x00);
+               REG_WR(bp, BNX2_CTX_PAGE_TBL, pcid_addr);
+
+               /* Zero out the context. */
+               for (offset = 0; offset < PHY_CTX_SIZE; offset += 4) {
+                       CTX_WR(bp, 0x00, offset, 0);
+               }
+
+               REG_WR(bp, BNX2_CTX_VIRT_ADDR, vcid_addr);
+               REG_WR(bp, BNX2_CTX_PAGE_TBL, pcid_addr);
+       }
+}
+
+static int
+bnx2_alloc_bad_rbuf(struct bnx2 *bp)
+{
+       u16 *good_mbuf;
+       u32 good_mbuf_cnt;
+       u32 val;
+
+       good_mbuf = kmalloc(512 * sizeof(u16), GFP_KERNEL);
+       if (good_mbuf == NULL) {
+               printk(KERN_ERR PFX "Failed to allocate memory in "
+                                   "bnx2_alloc_bad_rbuf\n");
+               return -ENOMEM;
+       }
+
+       REG_WR(bp, BNX2_MISC_ENABLE_SET_BITS,
+               BNX2_MISC_ENABLE_SET_BITS_RX_MBUF_ENABLE);
+
+       good_mbuf_cnt = 0;
+
+       /* Allocate a bunch of mbufs and save the good ones in an array. */
+       val = REG_RD_IND(bp, BNX2_RBUF_STATUS1);
+       while (val & BNX2_RBUF_STATUS1_FREE_COUNT) {
+               REG_WR_IND(bp, BNX2_RBUF_COMMAND, BNX2_RBUF_COMMAND_ALLOC_REQ);
+
+               val = REG_RD_IND(bp, BNX2_RBUF_FW_BUF_ALLOC);
+
+               val &= BNX2_RBUF_FW_BUF_ALLOC_VALUE;
+
+               /* The addresses with Bit 9 set are bad memory blocks. */
+               if (!(val & (1 << 9))) {
+                       good_mbuf[good_mbuf_cnt] = (u16) val;
+                       good_mbuf_cnt++;
+               }
+
+               val = REG_RD_IND(bp, BNX2_RBUF_STATUS1);
+       }
+
+       /* Free the good ones back to the mbuf pool thus discarding
+        * all the bad ones. */
+       while (good_mbuf_cnt) {
+               good_mbuf_cnt--;
+
+               val = good_mbuf[good_mbuf_cnt];
+               val = (val << 9) | val | 1;
+
+               REG_WR_IND(bp, BNX2_RBUF_FW_BUF_FREE, val);
+       }
+       kfree(good_mbuf);
+       return 0;
+}
+
+static void
+bnx2_set_mac_addr(struct bnx2 *bp) 
+{
+       u32 val;
+       u8 *mac_addr = bp->dev->dev_addr;
+
+       val = (mac_addr[0] << 8) | mac_addr[1];
+
+       REG_WR(bp, BNX2_EMAC_MAC_MATCH0, val);
+
+       val = (mac_addr[2] << 24) | (mac_addr[3] << 16) | 
+               (mac_addr[4] << 8) | mac_addr[5];
+
+       REG_WR(bp, BNX2_EMAC_MAC_MATCH1, val);
+}
+
+static inline int
+bnx2_alloc_rx_skb(struct bnx2 *bp, u16 index)
+{
+       struct sk_buff *skb;
+       struct sw_bd *rx_buf = &bp->rx_buf_ring[index];
+       dma_addr_t mapping;
+       struct rx_bd *rxbd = &bp->rx_desc_ring[index];
+       unsigned long align;
+
+       skb = dev_alloc_skb(bp->rx_buf_size);
+       if (skb == NULL) {
+               return -ENOMEM;
+       }
+
+       if (unlikely((align = (unsigned long) skb->data & 0x7))) {
+               skb_reserve(skb, 8 - align);
+       }
+
+       skb->dev = bp->dev;
+       mapping = pci_map_single(bp->pdev, skb->data, bp->rx_buf_use_size,
+               PCI_DMA_FROMDEVICE);
+
+       rx_buf->skb = skb;
+       pci_unmap_addr_set(rx_buf, mapping, mapping);
+
+       rxbd->rx_bd_haddr_hi = (u64) mapping >> 32;
+       rxbd->rx_bd_haddr_lo = (u64) mapping & 0xffffffff;
+
+       bp->rx_prod_bseq += bp->rx_buf_use_size;
+
+       return 0;
+}
+
+static void
+bnx2_phy_int(struct bnx2 *bp)
+{
+       u32 new_link_state, old_link_state;
+
+       new_link_state = bp->status_blk->status_attn_bits &
+               STATUS_ATTN_BITS_LINK_STATE;
+       old_link_state = bp->status_blk->status_attn_bits_ack &
+               STATUS_ATTN_BITS_LINK_STATE;
+       if (new_link_state != old_link_state) {
+               if (new_link_state) {
+                       REG_WR(bp, BNX2_PCICFG_STATUS_BIT_SET_CMD,
+                               STATUS_ATTN_BITS_LINK_STATE);
+               }
+               else {
+                       REG_WR(bp, BNX2_PCICFG_STATUS_BIT_CLEAR_CMD,
+                               STATUS_ATTN_BITS_LINK_STATE);
+               }
+               bnx2_set_link(bp);
+       }
+}
+
+static void
+bnx2_tx_int(struct bnx2 *bp)
+{
+       u16 hw_cons, sw_cons, sw_ring_cons;
+       int tx_free_bd = 0;
+
+       hw_cons = bp->status_blk->status_tx_quick_consumer_index0;
+       if ((hw_cons & MAX_TX_DESC_CNT) == MAX_TX_DESC_CNT) {
+               hw_cons++;
+       }
+       sw_cons = bp->tx_cons;
+
+       while (sw_cons != hw_cons) {
+               struct sw_bd *tx_buf;
+               struct sk_buff *skb;
+               int i, last;
+
+               sw_ring_cons = TX_RING_IDX(sw_cons);
+
+               tx_buf = &bp->tx_buf_ring[sw_ring_cons];
+               skb = tx_buf->skb;
+#ifdef BCM_TSO 
+               /* partial BD completions possible with TSO packets */
+               if (skb_shinfo(skb)->tso_size) {
+                       u16 last_idx, last_ring_idx;
+
+                       last_idx = sw_cons +
+                               skb_shinfo(skb)->nr_frags + 1;
+                       last_ring_idx = sw_ring_cons +
+                               skb_shinfo(skb)->nr_frags + 1;
+                       if (unlikely(last_ring_idx >= MAX_TX_DESC_CNT)) {
+                               last_idx++;
+                       }
+                       if (((s16) ((s16) last_idx - (s16) hw_cons)) > 0) {
+                               break;
+                       }
+               }
+#endif
+               pci_unmap_single(bp->pdev, pci_unmap_addr(tx_buf, mapping),
+                       skb_headlen(skb), PCI_DMA_TODEVICE);
+
+               tx_buf->skb = NULL;
+               last = skb_shinfo(skb)->nr_frags;
+
+               for (i = 0; i < last; i++) {
+                       sw_cons = NEXT_TX_BD(sw_cons);
+
+                       pci_unmap_page(bp->pdev,
+                               pci_unmap_addr(
+                                       &bp->tx_buf_ring[TX_RING_IDX(sw_cons)],
+                                       mapping),
+                               skb_shinfo(skb)->frags[i].size,
+                               PCI_DMA_TODEVICE);
+               }
+
+               sw_cons = NEXT_TX_BD(sw_cons);
+
+               tx_free_bd += last + 1;
+
+               dev_kfree_skb_irq(skb);
+
+               hw_cons = bp->status_blk->status_tx_quick_consumer_index0;
+               if ((hw_cons & MAX_TX_DESC_CNT) == MAX_TX_DESC_CNT) {
+                       hw_cons++;
+               }
+       }
+
+       atomic_add(tx_free_bd, &bp->tx_avail_bd);
+
+       if (unlikely(netif_queue_stopped(bp->dev))) {
+               unsigned long flags;
+
+               spin_lock_irqsave(&bp->tx_lock, flags);
+               if ((netif_queue_stopped(bp->dev)) &&
+                       (atomic_read(&bp->tx_avail_bd) > MAX_SKB_FRAGS)) {
+
+                       netif_wake_queue(bp->dev);
+               }
+               spin_unlock_irqrestore(&bp->tx_lock, flags);
+       }
+
+       bp->tx_cons = sw_cons;
+
+}
+
+static inline void
+bnx2_reuse_rx_skb(struct bnx2 *bp, struct sk_buff *skb,
+       u16 cons, u16 prod)
+{
+       struct sw_bd *cons_rx_buf = &bp->rx_buf_ring[cons];
+       struct sw_bd *prod_rx_buf = &bp->rx_buf_ring[prod];
+       struct rx_bd *cons_bd = &bp->rx_desc_ring[cons];
+       struct rx_bd *prod_bd = &bp->rx_desc_ring[prod];
+
+       pci_dma_sync_single_for_device(bp->pdev,
+               pci_unmap_addr(cons_rx_buf, mapping),
+               bp->rx_offset + RX_COPY_THRESH, PCI_DMA_FROMDEVICE);
+
+       prod_rx_buf->skb = cons_rx_buf->skb;
+       pci_unmap_addr_set(prod_rx_buf, mapping,
+                       pci_unmap_addr(cons_rx_buf, mapping));
+
+       memcpy(prod_bd, cons_bd, 8);
+
+       bp->rx_prod_bseq += bp->rx_buf_use_size;
+
+}
+
+static int
+bnx2_rx_int(struct bnx2 *bp, int budget)
+{
+       u16 hw_cons, sw_cons, sw_ring_cons, sw_prod, sw_ring_prod;
+       struct l2_fhdr *rx_hdr;
+       int rx_pkt = 0;
+
+       hw_cons = bp->status_blk->status_rx_quick_consumer_index0;
+       if ((hw_cons & MAX_RX_DESC_CNT) == MAX_RX_DESC_CNT) {
+               hw_cons++;
+       }
+       sw_cons = bp->rx_cons;
+       sw_prod = bp->rx_prod;
+
+       /* Memory barrier necessary as speculative reads of the rx
+        * buffer can be ahead of the index in the status block
+        */
+       rmb();
+       while (sw_cons != hw_cons) {
+               unsigned int len;
+               u16 status;
+               struct sw_bd *rx_buf;
+               struct sk_buff *skb;
+
+               sw_ring_cons = RX_RING_IDX(sw_cons);
+               sw_ring_prod = RX_RING_IDX(sw_prod);
+
+               rx_buf = &bp->rx_buf_ring[sw_ring_cons];
+               skb = rx_buf->skb;
+               pci_dma_sync_single_for_cpu(bp->pdev,
+                       pci_unmap_addr(rx_buf, mapping),
+                       bp->rx_offset + RX_COPY_THRESH, PCI_DMA_FROMDEVICE);
+
+               rx_hdr = (struct l2_fhdr *) skb->data;
+               len = rx_hdr->l2_fhdr_pkt_len - 4;
+
+               if (rx_hdr->l2_fhdr_errors &
+                       (L2_FHDR_ERRORS_BAD_CRC |
+                       L2_FHDR_ERRORS_PHY_DECODE |
+                       L2_FHDR_ERRORS_ALIGNMENT |
+                       L2_FHDR_ERRORS_TOO_SHORT |
+                       L2_FHDR_ERRORS_GIANT_FRAME)) {
+
+                       goto reuse_rx;
+               }
+
+               /* Since we don't have a jumbo ring, copy small packets
+                * if mtu > 1500
+                */
+               if ((bp->dev->mtu > 1500) && (len <= RX_COPY_THRESH)) {
+                       struct sk_buff *new_skb;
+
+                       new_skb = dev_alloc_skb(len + 2);
+                       if (new_skb == NULL)
+                               goto reuse_rx;
+
+                       /* aligned copy */
+                       memcpy(new_skb->data,
+                               skb->data + bp->rx_offset - 2,
+                               len + 2);
+
+                       skb_reserve(new_skb, 2);
+                       skb_put(new_skb, len);
+                       new_skb->dev = bp->dev;
+
+                       bnx2_reuse_rx_skb(bp, skb,
+                               sw_ring_cons, sw_ring_prod);
+
+                       skb = new_skb;
+               }
+               else if (bnx2_alloc_rx_skb(bp, sw_ring_prod) == 0) {
+                       pci_unmap_single(bp->pdev,
+                               pci_unmap_addr(rx_buf, mapping),
+                               bp->rx_buf_use_size, PCI_DMA_FROMDEVICE);
+
+                       skb_reserve(skb, bp->rx_offset);
+                       skb_put(skb, len);
+               }
+               else {
+reuse_rx:
+                       bnx2_reuse_rx_skb(bp, skb,
+                               sw_ring_cons, sw_ring_prod);
+                       goto next_rx;
+               }
+
+               skb->protocol = eth_type_trans(skb, bp->dev);
+
+               if ((len > (bp->dev->mtu + ETH_HLEN)) &&
+                       (htons(skb->protocol) != 0x8100)) {
+
+                       dev_kfree_skb_irq(skb);
+                       goto next_rx;
+
+               }
+
+               status = rx_hdr->l2_fhdr_status;
+               skb->ip_summed = CHECKSUM_NONE;
+               if (bp->rx_csum &&
+                       (status & (L2_FHDR_STATUS_TCP_SEGMENT |
+                       L2_FHDR_STATUS_UDP_DATAGRAM))) {
+
+                       u16 cksum = rx_hdr->l2_fhdr_tcp_udp_xsum;
+
+                       if (cksum == 0xffff)
+                               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               }
+
+#ifdef BCM_VLAN
+               if ((status & L2_FHDR_STATUS_L2_VLAN_TAG) && (bp->vlgrp != 0)) {
+                       vlan_hwaccel_receive_skb(skb, bp->vlgrp,
+                               rx_hdr->l2_fhdr_vlan_tag);
+               }
+               else
+#endif
+                       netif_receive_skb(skb);
+
+               bp->dev->last_rx = jiffies;
+               rx_pkt++;
+
+next_rx:
+               rx_buf->skb = NULL;
+
+               sw_cons = NEXT_RX_BD(sw_cons);
+               sw_prod = NEXT_RX_BD(sw_prod);
+
+               if ((rx_pkt == budget))
+                       break;
+       }
+       bp->rx_cons = sw_cons;
+       bp->rx_prod = sw_prod;
+
+       REG_WR16(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BDIDX, sw_prod);
+
+       REG_WR(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BSEQ, bp->rx_prod_bseq);
+
+       mmiowb();
+
+       return rx_pkt;
+
+}
+
+/* MSI ISR - The only difference between this and the INTx ISR
+ * is that the MSI interrupt is always serviced.
+ */
+static irqreturn_t
+bnx2_msi(int irq, void *dev_instance, struct pt_regs *regs)
+{
+       struct net_device *dev = dev_instance;
+       struct bnx2 *bp = dev->priv;
+
+       REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
+               BNX2_PCICFG_INT_ACK_CMD_USE_INT_HC_PARAM |
+               BNX2_PCICFG_INT_ACK_CMD_MASK_INT);
+
+       /* Return here if interrupt is disabled. */
+       if (unlikely(atomic_read(&bp->intr_sem) != 0)) {
+               return IRQ_RETVAL(1);
+       }
+
+       if (netif_rx_schedule_prep(dev)) {
+               __netif_rx_schedule(dev);
+       }
+
+       return IRQ_RETVAL(1);
+}
+
+static irqreturn_t
+bnx2_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+{
+       struct net_device *dev = dev_instance;
+       struct bnx2 *bp = dev->priv;
+
+       /* When using INTx, it is possible for the interrupt to arrive
+        * at the CPU before the status block posted prior to the
+        * interrupt. Reading a register will flush the status block.
+        * When using MSI, the MSI message will always complete after
+        * the status block write.
+        */
+       if ((bp->status_blk->status_idx == bp->last_status_idx) ||
+           (REG_RD(bp, BNX2_PCICFG_MISC_STATUS) &
+            BNX2_PCICFG_MISC_STATUS_INTA_VALUE))
+               return IRQ_RETVAL(0);
+
+       REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
+               BNX2_PCICFG_INT_ACK_CMD_USE_INT_HC_PARAM |
+               BNX2_PCICFG_INT_ACK_CMD_MASK_INT);
+
+       /* Return here if interrupt is shared and is disabled. */
+       if (unlikely(atomic_read(&bp->intr_sem) != 0)) {
+               return IRQ_RETVAL(1);
+       }
+
+       if (netif_rx_schedule_prep(dev)) {
+               __netif_rx_schedule(dev);
+       }
+
+       return IRQ_RETVAL(1);
+}
+
+static int
+bnx2_poll(struct net_device *dev, int *budget)
+{
+       struct bnx2 *bp = dev->priv;
+       int rx_done = 1;
+
+       bp->last_status_idx = bp->status_blk->status_idx;
+
+       rmb();
+       if ((bp->status_blk->status_attn_bits &
+               STATUS_ATTN_BITS_LINK_STATE) !=
+               (bp->status_blk->status_attn_bits_ack &
+               STATUS_ATTN_BITS_LINK_STATE)) {
+
+               unsigned long flags;
+
+               spin_lock_irqsave(&bp->phy_lock, flags);
+               bnx2_phy_int(bp);
+               spin_unlock_irqrestore(&bp->phy_lock, flags);
+       }
+
+       if (bp->status_blk->status_tx_quick_consumer_index0 != bp->tx_cons) {
+               bnx2_tx_int(bp);
+       }
+
+       if (bp->status_blk->status_rx_quick_consumer_index0 != bp->rx_cons) {
+               int orig_budget = *budget;
+               int work_done;
+
+               if (orig_budget > dev->quota)
+                       orig_budget = dev->quota;
+               
+               work_done = bnx2_rx_int(bp, orig_budget);
+               *budget -= work_done;
+               dev->quota -= work_done;
+               
+               if (work_done >= orig_budget) {
+                       rx_done = 0;
+               }
+       }
+       
+       if (rx_done) {
+               netif_rx_complete(dev);
+               REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
+                       BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID |
+                       bp->last_status_idx);
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Called with rtnl_lock from vlan functions and also dev->xmit_lock
+ * from set_multicast.
+ */
+static void
+bnx2_set_rx_mode(struct net_device *dev)
+{
+       struct bnx2 *bp = dev->priv;
+       u32 rx_mode, sort_mode;
+       int i;
+       unsigned long flags;
+
+       spin_lock_irqsave(&bp->phy_lock, flags);
+
+       rx_mode = bp->rx_mode & ~(BNX2_EMAC_RX_MODE_PROMISCUOUS |
+                                 BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG);
+       sort_mode = 1 | BNX2_RPM_SORT_USER0_BC_EN;
+#ifdef BCM_VLAN
+       if (!bp->vlgrp) {
+               rx_mode |= BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG;
+       }
+#else
+       rx_mode |= BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG;
+#endif
+       if (dev->flags & IFF_PROMISC) {
+               /* Promiscuous mode. */
+               rx_mode |= BNX2_EMAC_RX_MODE_PROMISCUOUS;
+               sort_mode |= BNX2_RPM_SORT_USER0_PROM_EN;
+       }
+       else if (dev->flags & IFF_ALLMULTI) {
+               for (i = 0; i < NUM_MC_HASH_REGISTERS; i++) {
+                       REG_WR(bp, BNX2_EMAC_MULTICAST_HASH0 + (i * 4),
+                              0xffffffff);
+               }
+               sort_mode |= BNX2_RPM_SORT_USER0_MC_EN;
+       }
+       else {
+               /* Accept one or more multicast(s). */
+               struct dev_mc_list *mclist;
+               u32 mc_filter[NUM_MC_HASH_REGISTERS];
+               u32 regidx;
+               u32 bit;
+               u32 crc;
+
+               memset(mc_filter, 0, 4 * NUM_MC_HASH_REGISTERS);
+
+               for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+                    i++, mclist = mclist->next) {
+
+                       crc = ether_crc_le(ETH_ALEN, mclist->dmi_addr);
+                       bit = crc & 0xff;
+                       regidx = (bit & 0xe0) >> 5;
+                       bit &= 0x1f;
+                       mc_filter[regidx] |= (1 << bit);
+               }
+
+               for (i = 0; i < NUM_MC_HASH_REGISTERS; i++) {
+                       REG_WR(bp, BNX2_EMAC_MULTICAST_HASH0 + (i * 4),
+                              mc_filter[i]);
+               }
+
+               sort_mode |= BNX2_RPM_SORT_USER0_MC_HSH_EN;
+       }
+
+       if (rx_mode != bp->rx_mode) {
+               bp->rx_mode = rx_mode;
+               REG_WR(bp, BNX2_EMAC_RX_MODE, rx_mode);
+       }
+
+       REG_WR(bp, BNX2_RPM_SORT_USER0, 0x0);
+       REG_WR(bp, BNX2_RPM_SORT_USER0, sort_mode);
+       REG_WR(bp, BNX2_RPM_SORT_USER0, sort_mode | BNX2_RPM_SORT_USER0_ENA);
+
+       spin_unlock_irqrestore(&bp->phy_lock, flags);
+}
+
+static void
+load_rv2p_fw(struct bnx2 *bp, u32 *rv2p_code, u32 rv2p_code_len,
+       u32 rv2p_proc)
+{
+       int i;
+       u32 val;
+
+
+       for (i = 0; i < rv2p_code_len; i += 8) {
+               REG_WR(bp, BNX2_RV2P_INSTR_HIGH, *rv2p_code);
+               rv2p_code++;
+               REG_WR(bp, BNX2_RV2P_INSTR_LOW, *rv2p_code);
+               rv2p_code++;
+
+               if (rv2p_proc == RV2P_PROC1) {
+                       val = (i / 8) | BNX2_RV2P_PROC1_ADDR_CMD_RDWR;
+                       REG_WR(bp, BNX2_RV2P_PROC1_ADDR_CMD, val);
+               }
+               else {
+                       val = (i / 8) | BNX2_RV2P_PROC2_ADDR_CMD_RDWR;
+                       REG_WR(bp, BNX2_RV2P_PROC2_ADDR_CMD, val);
+               }
+       }
+
+       /* Reset the processor, un-stall is done later. */
+       if (rv2p_proc == RV2P_PROC1) {
+               REG_WR(bp, BNX2_RV2P_COMMAND, BNX2_RV2P_COMMAND_PROC1_RESET);
+       }
+       else {
+               REG_WR(bp, BNX2_RV2P_COMMAND, BNX2_RV2P_COMMAND_PROC2_RESET);
+       }
+}
+
+static void
+load_cpu_fw(struct bnx2 *bp, struct cpu_reg *cpu_reg, struct fw_info *fw)
+{
+       u32 offset;
+       u32 val;
+
+       /* Halt the CPU. */
+       val = REG_RD_IND(bp, cpu_reg->mode);
+       val |= cpu_reg->mode_value_halt;
+       REG_WR_IND(bp, cpu_reg->mode, val);
+       REG_WR_IND(bp, cpu_reg->state, cpu_reg->state_value_clear);
+
+       /* Load the Text area. */
+       offset = cpu_reg->spad_base + (fw->text_addr - cpu_reg->mips_view_base);
+       if (fw->text) {
+               int j;
+
+               for (j = 0; j < (fw->text_len / 4); j++, offset += 4) {
+                       REG_WR_IND(bp, offset, fw->text[j]);
+               }
+       }
+
+       /* Load the Data area. */
+       offset = cpu_reg->spad_base + (fw->data_addr - cpu_reg->mips_view_base);
+       if (fw->data) {
+               int j;
+
+               for (j = 0; j < (fw->data_len / 4); j++, offset += 4) {
+                       REG_WR_IND(bp, offset, fw->data[j]);
+               }
+       }
+
+       /* Load the SBSS area. */
+       offset = cpu_reg->spad_base + (fw->sbss_addr - cpu_reg->mips_view_base);
+       if (fw->sbss) {
+               int j;
+
+               for (j = 0; j < (fw->sbss_len / 4); j++, offset += 4) {
+                       REG_WR_IND(bp, offset, fw->sbss[j]);
+               }
+       }
+
+       /* Load the BSS area. */
+       offset = cpu_reg->spad_base + (fw->bss_addr - cpu_reg->mips_view_base);
+       if (fw->bss) {
+               int j;
+
+               for (j = 0; j < (fw->bss_len/4); j++, offset += 4) {
+                       REG_WR_IND(bp, offset, fw->bss[j]);
+               }
+       }
+
+       /* Load the Read-Only area. */
+       offset = cpu_reg->spad_base +
+               (fw->rodata_addr - cpu_reg->mips_view_base);
+       if (fw->rodata) {
+               int j;
+
+               for (j = 0; j < (fw->rodata_len / 4); j++, offset += 4) {
+                       REG_WR_IND(bp, offset, fw->rodata[j]);
+               }
+       }
+
+       /* Clear the pre-fetch instruction. */
+       REG_WR_IND(bp, cpu_reg->inst, 0);
+       REG_WR_IND(bp, cpu_reg->pc, fw->start_addr);
+
+       /* Start the CPU. */
+       val = REG_RD_IND(bp, cpu_reg->mode);
+       val &= ~cpu_reg->mode_value_halt;
+       REG_WR_IND(bp, cpu_reg->state, cpu_reg->state_value_clear);
+       REG_WR_IND(bp, cpu_reg->mode, val);
+}
+
+static void
+bnx2_init_cpus(struct bnx2 *bp)
+{
+       struct cpu_reg cpu_reg;
+       struct fw_info fw;
+
+       /* Initialize the RV2P processor. */
+       load_rv2p_fw(bp, bnx2_rv2p_proc1, sizeof(bnx2_rv2p_proc1), RV2P_PROC1);
+       load_rv2p_fw(bp, bnx2_rv2p_proc2, sizeof(bnx2_rv2p_proc2), RV2P_PROC2);
+
+       /* Initialize the RX Processor. */
+       cpu_reg.mode = BNX2_RXP_CPU_MODE;
+       cpu_reg.mode_value_halt = BNX2_RXP_CPU_MODE_SOFT_HALT;
+       cpu_reg.mode_value_sstep = BNX2_RXP_CPU_MODE_STEP_ENA;
+       cpu_reg.state = BNX2_RXP_CPU_STATE;
+       cpu_reg.state_value_clear = 0xffffff;
+       cpu_reg.gpr0 = BNX2_RXP_CPU_REG_FILE;
+       cpu_reg.evmask = BNX2_RXP_CPU_EVENT_MASK;
+       cpu_reg.pc = BNX2_RXP_CPU_PROGRAM_COUNTER;
+       cpu_reg.inst = BNX2_RXP_CPU_INSTRUCTION;
+       cpu_reg.bp = BNX2_RXP_CPU_HW_BREAKPOINT;
+       cpu_reg.spad_base = BNX2_RXP_SCRATCH;
+       cpu_reg.mips_view_base = 0x8000000;
+    
+       fw.ver_major = bnx2_RXP_b06FwReleaseMajor;
+       fw.ver_minor = bnx2_RXP_b06FwReleaseMinor;
+       fw.ver_fix = bnx2_RXP_b06FwReleaseFix;
+       fw.start_addr = bnx2_RXP_b06FwStartAddr;
+
+       fw.text_addr = bnx2_RXP_b06FwTextAddr;
+       fw.text_len = bnx2_RXP_b06FwTextLen;
+       fw.text_index = 0;
+       fw.text = bnx2_RXP_b06FwText;
+
+       fw.data_addr = bnx2_RXP_b06FwDataAddr;
+       fw.data_len = bnx2_RXP_b06FwDataLen;
+       fw.data_index = 0;
+       fw.data = bnx2_RXP_b06FwData;
+
+       fw.sbss_addr = bnx2_RXP_b06FwSbssAddr;
+       fw.sbss_len = bnx2_RXP_b06FwSbssLen;
+       fw.sbss_index = 0;
+       fw.sbss = bnx2_RXP_b06FwSbss;
+
+       fw.bss_addr = bnx2_RXP_b06FwBssAddr;
+       fw.bss_len = bnx2_RXP_b06FwBssLen;
+       fw.bss_index = 0;
+       fw.bss = bnx2_RXP_b06FwBss;
+
+       fw.rodata_addr = bnx2_RXP_b06FwRodataAddr;
+       fw.rodata_len = bnx2_RXP_b06FwRodataLen;
+       fw.rodata_index = 0;
+       fw.rodata = bnx2_RXP_b06FwRodata;
+
+       load_cpu_fw(bp, &cpu_reg, &fw);
+
+       /* Initialize the TX Processor. */
+       cpu_reg.mode = BNX2_TXP_CPU_MODE;
+       cpu_reg.mode_value_halt = BNX2_TXP_CPU_MODE_SOFT_HALT;
+       cpu_reg.mode_value_sstep = BNX2_TXP_CPU_MODE_STEP_ENA;
+       cpu_reg.state = BNX2_TXP_CPU_STATE;
+       cpu_reg.state_value_clear = 0xffffff;
+       cpu_reg.gpr0 = BNX2_TXP_CPU_REG_FILE;
+       cpu_reg.evmask = BNX2_TXP_CPU_EVENT_MASK;
+       cpu_reg.pc = BNX2_TXP_CPU_PROGRAM_COUNTER;
+       cpu_reg.inst = BNX2_TXP_CPU_INSTRUCTION;
+       cpu_reg.bp = BNX2_TXP_CPU_HW_BREAKPOINT;
+       cpu_reg.spad_base = BNX2_TXP_SCRATCH;
+       cpu_reg.mips_view_base = 0x8000000;
+    
+       fw.ver_major = bnx2_TXP_b06FwReleaseMajor;
+       fw.ver_minor = bnx2_TXP_b06FwReleaseMinor;
+       fw.ver_fix = bnx2_TXP_b06FwReleaseFix;
+       fw.start_addr = bnx2_TXP_b06FwStartAddr;
+
+       fw.text_addr = bnx2_TXP_b06FwTextAddr;
+       fw.text_len = bnx2_TXP_b06FwTextLen;
+       fw.text_index = 0;
+       fw.text = bnx2_TXP_b06FwText;
+
+       fw.data_addr = bnx2_TXP_b06FwDataAddr;
+       fw.data_len = bnx2_TXP_b06FwDataLen;
+       fw.data_index = 0;
+       fw.data = bnx2_TXP_b06FwData;
+
+       fw.sbss_addr = bnx2_TXP_b06FwSbssAddr;
+       fw.sbss_len = bnx2_TXP_b06FwSbssLen;
+       fw.sbss_index = 0;
+       fw.sbss = bnx2_TXP_b06FwSbss;
+
+       fw.bss_addr = bnx2_TXP_b06FwBssAddr;
+       fw.bss_len = bnx2_TXP_b06FwBssLen;
+       fw.bss_index = 0;
+       fw.bss = bnx2_TXP_b06FwBss;
+
+       fw.rodata_addr = bnx2_TXP_b06FwRodataAddr;
+       fw.rodata_len = bnx2_TXP_b06FwRodataLen;
+       fw.rodata_index = 0;
+       fw.rodata = bnx2_TXP_b06FwRodata;
+
+       load_cpu_fw(bp, &cpu_reg, &fw);
+
+       /* Initialize the TX Patch-up Processor. */
+       cpu_reg.mode = BNX2_TPAT_CPU_MODE;
+       cpu_reg.mode_value_halt = BNX2_TPAT_CPU_MODE_SOFT_HALT;
+       cpu_reg.mode_value_sstep = BNX2_TPAT_CPU_MODE_STEP_ENA;
+       cpu_reg.state = BNX2_TPAT_CPU_STATE;
+       cpu_reg.state_value_clear = 0xffffff;
+       cpu_reg.gpr0 = BNX2_TPAT_CPU_REG_FILE;
+       cpu_reg.evmask = BNX2_TPAT_CPU_EVENT_MASK;
+       cpu_reg.pc = BNX2_TPAT_CPU_PROGRAM_COUNTER;
+       cpu_reg.inst = BNX2_TPAT_CPU_INSTRUCTION;
+       cpu_reg.bp = BNX2_TPAT_CPU_HW_BREAKPOINT;
+       cpu_reg.spad_base = BNX2_TPAT_SCRATCH;
+       cpu_reg.mips_view_base = 0x8000000;
+    
+       fw.ver_major = bnx2_TPAT_b06FwReleaseMajor;
+       fw.ver_minor = bnx2_TPAT_b06FwReleaseMinor;
+       fw.ver_fix = bnx2_TPAT_b06FwReleaseFix;
+       fw.start_addr = bnx2_TPAT_b06FwStartAddr;
+
+       fw.text_addr = bnx2_TPAT_b06FwTextAddr;
+       fw.text_len = bnx2_TPAT_b06FwTextLen;
+       fw.text_index = 0;
+       fw.text = bnx2_TPAT_b06FwText;
+
+       fw.data_addr = bnx2_TPAT_b06FwDataAddr;
+       fw.data_len = bnx2_TPAT_b06FwDataLen;
+       fw.data_index = 0;
+       fw.data = bnx2_TPAT_b06FwData;
+
+       fw.sbss_addr = bnx2_TPAT_b06FwSbssAddr;
+       fw.sbss_len = bnx2_TPAT_b06FwSbssLen;
+       fw.sbss_index = 0;
+       fw.sbss = bnx2_TPAT_b06FwSbss;
+
+       fw.bss_addr = bnx2_TPAT_b06FwBssAddr;
+       fw.bss_len = bnx2_TPAT_b06FwBssLen;
+       fw.bss_index = 0;
+       fw.bss = bnx2_TPAT_b06FwBss;
+
+       fw.rodata_addr = bnx2_TPAT_b06FwRodataAddr;
+       fw.rodata_len = bnx2_TPAT_b06FwRodataLen;
+       fw.rodata_index = 0;
+       fw.rodata = bnx2_TPAT_b06FwRodata;
+
+       load_cpu_fw(bp, &cpu_reg, &fw);
+
+       /* Initialize the Completion Processor. */
+       cpu_reg.mode = BNX2_COM_CPU_MODE;
+       cpu_reg.mode_value_halt = BNX2_COM_CPU_MODE_SOFT_HALT;
+       cpu_reg.mode_value_sstep = BNX2_COM_CPU_MODE_STEP_ENA;
+       cpu_reg.state = BNX2_COM_CPU_STATE;
+       cpu_reg.state_value_clear = 0xffffff;
+       cpu_reg.gpr0 = BNX2_COM_CPU_REG_FILE;
+       cpu_reg.evmask = BNX2_COM_CPU_EVENT_MASK;
+       cpu_reg.pc = BNX2_COM_CPU_PROGRAM_COUNTER;
+       cpu_reg.inst = BNX2_COM_CPU_INSTRUCTION;
+       cpu_reg.bp = BNX2_COM_CPU_HW_BREAKPOINT;
+       cpu_reg.spad_base = BNX2_COM_SCRATCH;
+       cpu_reg.mips_view_base = 0x8000000;
+    
+       fw.ver_major = bnx2_COM_b06FwReleaseMajor;
+       fw.ver_minor = bnx2_COM_b06FwReleaseMinor;
+       fw.ver_fix = bnx2_COM_b06FwReleaseFix;
+       fw.start_addr = bnx2_COM_b06FwStartAddr;
+
+       fw.text_addr = bnx2_COM_b06FwTextAddr;
+       fw.text_len = bnx2_COM_b06FwTextLen;
+       fw.text_index = 0;
+       fw.text = bnx2_COM_b06FwText;
+
+       fw.data_addr = bnx2_COM_b06FwDataAddr;
+       fw.data_len = bnx2_COM_b06FwDataLen;
+       fw.data_index = 0;
+       fw.data = bnx2_COM_b06FwData;
+
+       fw.sbss_addr = bnx2_COM_b06FwSbssAddr;
+       fw.sbss_len = bnx2_COM_b06FwSbssLen;
+       fw.sbss_index = 0;
+       fw.sbss = bnx2_COM_b06FwSbss;
+
+       fw.bss_addr = bnx2_COM_b06FwBssAddr;
+       fw.bss_len = bnx2_COM_b06FwBssLen;
+       fw.bss_index = 0;
+       fw.bss = bnx2_COM_b06FwBss;
+
+       fw.rodata_addr = bnx2_COM_b06FwRodataAddr;
+       fw.rodata_len = bnx2_COM_b06FwRodataLen;
+       fw.rodata_index = 0;
+       fw.rodata = bnx2_COM_b06FwRodata;
+
+       load_cpu_fw(bp, &cpu_reg, &fw);
+
+}
+
+static int
+bnx2_set_power_state(struct bnx2 *bp, int state)
+{
+       u16 pmcsr;
+
+       pci_read_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL, &pmcsr);
+
+       switch (state) {
+       case 0: {
+               u32 val;
+
+               pci_write_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL,
+                       (pmcsr & ~PCI_PM_CTRL_STATE_MASK) |
+                       PCI_PM_CTRL_PME_STATUS);
+
+               if (pmcsr & PCI_PM_CTRL_STATE_MASK)
+                       /* delay required during transition out of D3hot */
+                       msleep(20);
+
+               val = REG_RD(bp, BNX2_EMAC_MODE);
+               val |= BNX2_EMAC_MODE_MPKT_RCVD | BNX2_EMAC_MODE_ACPI_RCVD;
+               val &= ~BNX2_EMAC_MODE_MPKT;
+               REG_WR(bp, BNX2_EMAC_MODE, val);
+
+               val = REG_RD(bp, BNX2_RPM_CONFIG);
+               val &= ~BNX2_RPM_CONFIG_ACPI_ENA;
+               REG_WR(bp, BNX2_RPM_CONFIG, val);
+               break;
+       }
+       case 3: {
+               int i;
+               u32 val, wol_msg;
+
+               if (bp->wol) {
+                       u32 advertising;
+                       u8 autoneg;
+
+                       autoneg = bp->autoneg;
+                       advertising = bp->advertising;
+
+                       bp->autoneg = AUTONEG_SPEED;
+                       bp->advertising = ADVERTISED_10baseT_Half |
+                               ADVERTISED_10baseT_Full |
+                               ADVERTISED_100baseT_Half |
+                               ADVERTISED_100baseT_Full |
+                               ADVERTISED_Autoneg;
+
+                       bnx2_setup_copper_phy(bp);
+
+                       bp->autoneg = autoneg;
+                       bp->advertising = advertising;
+
+                       bnx2_set_mac_addr(bp);
+
+                       val = REG_RD(bp, BNX2_EMAC_MODE);
+
+                       /* Enable port mode. */
+                       val &= ~BNX2_EMAC_MODE_PORT;
+                       val |= BNX2_EMAC_MODE_PORT_MII |
+                              BNX2_EMAC_MODE_MPKT_RCVD |
+                              BNX2_EMAC_MODE_ACPI_RCVD |
+                              BNX2_EMAC_MODE_FORCE_LINK |
+                              BNX2_EMAC_MODE_MPKT;
+
+                       REG_WR(bp, BNX2_EMAC_MODE, val);
+
+                       /* receive all multicast */
+                       for (i = 0; i < NUM_MC_HASH_REGISTERS; i++) {
+                               REG_WR(bp, BNX2_EMAC_MULTICAST_HASH0 + (i * 4),
+                                      0xffffffff);
+                       }
+                       REG_WR(bp, BNX2_EMAC_RX_MODE,
+                              BNX2_EMAC_RX_MODE_SORT_MODE);
+
+                       val = 1 | BNX2_RPM_SORT_USER0_BC_EN |
+                             BNX2_RPM_SORT_USER0_MC_EN;
+                       REG_WR(bp, BNX2_RPM_SORT_USER0, 0x0);
+                       REG_WR(bp, BNX2_RPM_SORT_USER0, val);
+                       REG_WR(bp, BNX2_RPM_SORT_USER0, val |
+                              BNX2_RPM_SORT_USER0_ENA);
+
+                       /* Need to enable EMAC and RPM for WOL. */
+                       REG_WR(bp, BNX2_MISC_ENABLE_SET_BITS,
+                              BNX2_MISC_ENABLE_SET_BITS_RX_PARSER_MAC_ENABLE |
+                              BNX2_MISC_ENABLE_SET_BITS_TX_HEADER_Q_ENABLE |
+                              BNX2_MISC_ENABLE_SET_BITS_EMAC_ENABLE);
+
+                       val = REG_RD(bp, BNX2_RPM_CONFIG);
+                       val &= ~BNX2_RPM_CONFIG_ACPI_ENA;
+                       REG_WR(bp, BNX2_RPM_CONFIG, val);
+
+                       wol_msg = BNX2_DRV_MSG_CODE_SUSPEND_WOL;
+               }
+               else {
+                       wol_msg = BNX2_DRV_MSG_CODE_SUSPEND_NO_WOL;
+               }
+
+               bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT3 | wol_msg);
+
+               pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+               if ((CHIP_ID(bp) == CHIP_ID_5706_A0) ||
+                   (CHIP_ID(bp) == CHIP_ID_5706_A1)) {
+
+                       if (bp->wol)
+                               pmcsr |= 3;
+               }
+               else {
+                       pmcsr |= 3;
+               }
+               if (bp->wol) {
+                       pmcsr |= PCI_PM_CTRL_PME_ENABLE;
+               }
+               pci_write_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL,
+                                     pmcsr);
+
+               /* No more memory access after this point until
+                * device is brought back to D0.
+                */
+               udelay(50);
+               break;
+       }
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int
+bnx2_acquire_nvram_lock(struct bnx2 *bp)
+{
+       u32 val;
+       int j;
+
+       /* Request access to the flash interface. */
+       REG_WR(bp, BNX2_NVM_SW_ARB, BNX2_NVM_SW_ARB_ARB_REQ_SET2);
+       for (j = 0; j < NVRAM_TIMEOUT_COUNT; j++) {
+               val = REG_RD(bp, BNX2_NVM_SW_ARB);
+               if (val & BNX2_NVM_SW_ARB_ARB_ARB2)
+                       break;
+
+               udelay(5);
+       }
+
+       if (j >= NVRAM_TIMEOUT_COUNT)
+               return -EBUSY;
+
+       return 0;
+}
+
+static int
+bnx2_release_nvram_lock(struct bnx2 *bp)
+{
+       int j;
+       u32 val;
+
+       /* Relinquish nvram interface. */
+       REG_WR(bp, BNX2_NVM_SW_ARB, BNX2_NVM_SW_ARB_ARB_REQ_CLR2);
+
+       for (j = 0; j < NVRAM_TIMEOUT_COUNT; j++) {
+               val = REG_RD(bp, BNX2_NVM_SW_ARB);
+               if (!(val & BNX2_NVM_SW_ARB_ARB_ARB2))
+                       break;
+
+               udelay(5);
+       }
+
+       if (j >= NVRAM_TIMEOUT_COUNT)
+               return -EBUSY;
+
+       return 0;
+}
+
+
+static int
+bnx2_enable_nvram_write(struct bnx2 *bp)
+{
+       u32 val;
+
+       val = REG_RD(bp, BNX2_MISC_CFG);
+       REG_WR(bp, BNX2_MISC_CFG, val | BNX2_MISC_CFG_NVM_WR_EN_PCI);
+
+       if (!bp->flash_info->buffered) {
+               int j;
+
+               REG_WR(bp, BNX2_NVM_COMMAND, BNX2_NVM_COMMAND_DONE);
+               REG_WR(bp, BNX2_NVM_COMMAND,
+                      BNX2_NVM_COMMAND_WREN | BNX2_NVM_COMMAND_DOIT);
+
+               for (j = 0; j < NVRAM_TIMEOUT_COUNT; j++) {
+                       udelay(5);
+
+                       val = REG_RD(bp, BNX2_NVM_COMMAND);
+                       if (val & BNX2_NVM_COMMAND_DONE)
+                               break;
+               }
+
+               if (j >= NVRAM_TIMEOUT_COUNT)
+                       return -EBUSY;
+       }
+       return 0;
+}
+
+static void
+bnx2_disable_nvram_write(struct bnx2 *bp)
+{
+       u32 val;
+
+       val = REG_RD(bp, BNX2_MISC_CFG);
+       REG_WR(bp, BNX2_MISC_CFG, val & ~BNX2_MISC_CFG_NVM_WR_EN);
+}
+
+
+static void
+bnx2_enable_nvram_access(struct bnx2 *bp)
+{
+       u32 val;
+
+       val = REG_RD(bp, BNX2_NVM_ACCESS_ENABLE);
+       /* Enable both bits, even on read. */
+       REG_WR(bp, BNX2_NVM_ACCESS_ENABLE, 
+              val | BNX2_NVM_ACCESS_ENABLE_EN | BNX2_NVM_ACCESS_ENABLE_WR_EN);
+}
+
+static void
+bnx2_disable_nvram_access(struct bnx2 *bp)
+{
+       u32 val;
+
+       val = REG_RD(bp, BNX2_NVM_ACCESS_ENABLE);
+       /* Disable both bits, even after read. */
+       REG_WR(bp, BNX2_NVM_ACCESS_ENABLE, 
+               val & ~(BNX2_NVM_ACCESS_ENABLE_EN |
+                       BNX2_NVM_ACCESS_ENABLE_WR_EN));
+}
+
+static int
+bnx2_nvram_erase_page(struct bnx2 *bp, u32 offset)
+{
+       u32 cmd;
+       int j;
+
+       if (bp->flash_info->buffered)
+               /* Buffered flash, no erase needed */
+               return 0;
+
+       /* Build an erase command */
+       cmd = BNX2_NVM_COMMAND_ERASE | BNX2_NVM_COMMAND_WR |
+             BNX2_NVM_COMMAND_DOIT;
+
+       /* Need to clear DONE bit separately. */
+       REG_WR(bp, BNX2_NVM_COMMAND, BNX2_NVM_COMMAND_DONE);
+
+       /* Address of the NVRAM to read from. */
+       REG_WR(bp, BNX2_NVM_ADDR, offset & BNX2_NVM_ADDR_NVM_ADDR_VALUE);
+
+       /* Issue an erase command. */
+       REG_WR(bp, BNX2_NVM_COMMAND, cmd);
+
+       /* Wait for completion. */
+       for (j = 0; j < NVRAM_TIMEOUT_COUNT; j++) {
+               u32 val;
+
+               udelay(5);
+
+               val = REG_RD(bp, BNX2_NVM_COMMAND);
+               if (val & BNX2_NVM_COMMAND_DONE)
+                       break;
+       }
+
+       if (j >= NVRAM_TIMEOUT_COUNT)
+               return -EBUSY;
+
+       return 0;
+}
+
+static int
+bnx2_nvram_read_dword(struct bnx2 *bp, u32 offset, u8 *ret_val, u32 cmd_flags)
+{
+       u32 cmd;
+       int j;
+
+       /* Build the command word. */
+       cmd = BNX2_NVM_COMMAND_DOIT | cmd_flags;
+
+       /* Calculate an offset of a buffered flash. */
+       if (bp->flash_info->buffered) {
+               offset = ((offset / bp->flash_info->page_size) <<
+                          bp->flash_info->page_bits) +
+                         (offset % bp->flash_info->page_size);
+       }
+
+       /* Need to clear DONE bit separately. */
+       REG_WR(bp, BNX2_NVM_COMMAND, BNX2_NVM_COMMAND_DONE);
+
+       /* Address of the NVRAM to read from. */
+       REG_WR(bp, BNX2_NVM_ADDR, offset & BNX2_NVM_ADDR_NVM_ADDR_VALUE);
+
+       /* Issue a read command. */
+       REG_WR(bp, BNX2_NVM_COMMAND, cmd);
+
+       /* Wait for completion. */
+       for (j = 0; j < NVRAM_TIMEOUT_COUNT; j++) {
+               u32 val;
+
+               udelay(5);
+
+               val = REG_RD(bp, BNX2_NVM_COMMAND);
+               if (val & BNX2_NVM_COMMAND_DONE) {
+                       val = REG_RD(bp, BNX2_NVM_READ);
+
+                       val = be32_to_cpu(val);
+                       memcpy(ret_val, &val, 4);
+                       break;
+               }
+       }
+       if (j >= NVRAM_TIMEOUT_COUNT)
+               return -EBUSY;
+
+       return 0;
+}
+
+
+static int
+bnx2_nvram_write_dword(struct bnx2 *bp, u32 offset, u8 *val, u32 cmd_flags)
+{
+       u32 cmd, val32;
+       int j;
+
+       /* Build the command word. */
+       cmd = BNX2_NVM_COMMAND_DOIT | BNX2_NVM_COMMAND_WR | cmd_flags;
+
+       /* Calculate an offset of a buffered flash. */
+       if (bp->flash_info->buffered) {
+               offset = ((offset / bp->flash_info->page_size) <<
+                         bp->flash_info->page_bits) +
+                        (offset % bp->flash_info->page_size);
+       }
+
+       /* Need to clear DONE bit separately. */
+       REG_WR(bp, BNX2_NVM_COMMAND, BNX2_NVM_COMMAND_DONE);
+
+       memcpy(&val32, val, 4);
+       val32 = cpu_to_be32(val32);
+
+       /* Write the data. */
+       REG_WR(bp, BNX2_NVM_WRITE, val32);
+
+       /* Address of the NVRAM to write to. */
+       REG_WR(bp, BNX2_NVM_ADDR, offset & BNX2_NVM_ADDR_NVM_ADDR_VALUE);
+
+       /* Issue the write command. */
+       REG_WR(bp, BNX2_NVM_COMMAND, cmd);
+
+       /* Wait for completion. */
+       for (j = 0; j < NVRAM_TIMEOUT_COUNT; j++) {
+               udelay(5);
+
+               if (REG_RD(bp, BNX2_NVM_COMMAND) & BNX2_NVM_COMMAND_DONE)
+                       break;
+       }
+       if (j >= NVRAM_TIMEOUT_COUNT)
+               return -EBUSY;
+
+       return 0;
+}
+
+static int
+bnx2_init_nvram(struct bnx2 *bp)
+{
+       u32 val;
+       int j, entry_count, rc;
+       struct flash_spec *flash;
+
+       /* Determine the selected interface. */
+       val = REG_RD(bp, BNX2_NVM_CFG1);
+
+       entry_count = sizeof(flash_table) / sizeof(struct flash_spec);
+
+       rc = 0;
+       if (val & 0x40000000) {
+
+               /* Flash interface has been reconfigured */
+               for (j = 0, flash = &flash_table[0]; j < entry_count;
+                       j++, flash++) {
+
+                       if (val == flash->config1) {
+                               bp->flash_info = flash;
+                               break;
+                       }
+               }
+       }
+       else {
+               /* Not yet been reconfigured */
+
+               for (j = 0, flash = &flash_table[0]; j < entry_count;
+                       j++, flash++) {
+
+                       if ((val & FLASH_STRAP_MASK) == flash->strapping) {
+                               bp->flash_info = flash;
+
+                               /* Request access to the flash interface. */
+                               if ((rc = bnx2_acquire_nvram_lock(bp)) != 0)
+                                       return rc;
+
+                               /* Enable access to flash interface */
+                               bnx2_enable_nvram_access(bp);
+
+                               /* Reconfigure the flash interface */
+                               REG_WR(bp, BNX2_NVM_CFG1, flash->config1);
+                               REG_WR(bp, BNX2_NVM_CFG2, flash->config2);
+                               REG_WR(bp, BNX2_NVM_CFG3, flash->config3);
+                               REG_WR(bp, BNX2_NVM_WRITE1, flash->write1);
+
+                               /* Disable access to flash interface */
+                               bnx2_disable_nvram_access(bp);
+                               bnx2_release_nvram_lock(bp);
+
+                               break;
+                       }
+               }
+       } /* if (val & 0x40000000) */
+
+       if (j == entry_count) {
+               bp->flash_info = NULL;
+               printk(KERN_ALERT "Unknown flash/EEPROM type.\n");
+               rc = -ENODEV;
+       }
+
+       return rc;
+}
+
+static int
+bnx2_nvram_read(struct bnx2 *bp, u32 offset, u8 *ret_buf,
+               int buf_size)
+{
+       int rc = 0;
+       u32 cmd_flags, offset32, len32, extra;
+
+       if (buf_size == 0)
+               return 0;
+
+       /* Request access to the flash interface. */
+       if ((rc = bnx2_acquire_nvram_lock(bp)) != 0)
+               return rc;
+
+       /* Enable access to flash interface */
+       bnx2_enable_nvram_access(bp);
+
+       len32 = buf_size;
+       offset32 = offset;
+       extra = 0;
+
+       cmd_flags = 0;
+
+       if (offset32 & 3) {
+               u8 buf[4];
+               u32 pre_len;
+
+               offset32 &= ~3;
+               pre_len = 4 - (offset & 3);
+
+               if (pre_len >= len32) {
+                       pre_len = len32;
+                       cmd_flags = BNX2_NVM_COMMAND_FIRST |
+                                   BNX2_NVM_COMMAND_LAST;
+               }
+               else {
+                       cmd_flags = BNX2_NVM_COMMAND_FIRST;
+               }
+
+               rc = bnx2_nvram_read_dword(bp, offset32, buf, cmd_flags);
+
+               if (rc)
+                       return rc;
+
+               memcpy(ret_buf, buf + (offset & 3), pre_len);
+
+               offset32 += 4;
+               ret_buf += pre_len;
+               len32 -= pre_len;
+       }
+       if (len32 & 3) {
+               extra = 4 - (len32 & 3);
+               len32 = (len32 + 4) & ~3;
+       }
+
+       if (len32 == 4) {
+               u8 buf[4];
+
+               if (cmd_flags)
+                       cmd_flags = BNX2_NVM_COMMAND_LAST;
+               else
+                       cmd_flags = BNX2_NVM_COMMAND_FIRST |
+                                   BNX2_NVM_COMMAND_LAST;
+
+               rc = bnx2_nvram_read_dword(bp, offset32, buf, cmd_flags);
+
+               memcpy(ret_buf, buf, 4 - extra);
+       }
+       else if (len32 > 0) {
+               u8 buf[4];
+
+               /* Read the first word. */
+               if (cmd_flags)
+                       cmd_flags = 0;
+               else
+                       cmd_flags = BNX2_NVM_COMMAND_FIRST;
+
+               rc = bnx2_nvram_read_dword(bp, offset32, ret_buf, cmd_flags);
+
+               /* Advance to the next dword. */
+               offset32 += 4;
+               ret_buf += 4;
+               len32 -= 4;
+
+               while (len32 > 4 && rc == 0) {
+                       rc = bnx2_nvram_read_dword(bp, offset32, ret_buf, 0);
+
+                       /* Advance to the next dword. */
+                       offset32 += 4;
+                       ret_buf += 4;
+                       len32 -= 4;
+               }
+
+               if (rc)
+                       return rc;
+
+               cmd_flags = BNX2_NVM_COMMAND_LAST;
+               rc = bnx2_nvram_read_dword(bp, offset32, buf, cmd_flags);
+
+               memcpy(ret_buf, buf, 4 - extra);
+       }
+
+       /* Disable access to flash interface */
+       bnx2_disable_nvram_access(bp);
+
+       bnx2_release_nvram_lock(bp);
+
+       return rc;
+}
+
+static int
+bnx2_nvram_write(struct bnx2 *bp, u32 offset, u8 *data_buf,
+               int buf_size)
+{
+       u32 written, offset32, len32;
+       u8 *buf, start[4], end[4];
+       int rc = 0;
+       int align_start, align_end;
+
+       buf = data_buf;
+       offset32 = offset;
+       len32 = buf_size;
+       align_start = align_end = 0;
+
+       if ((align_start = (offset32 & 3))) {
+               offset32 &= ~3;
+               len32 += align_start;
+               if ((rc = bnx2_nvram_read(bp, offset32, start, 4)))
+                       return rc;
+       }
+
+       if (len32 & 3) {
+               if ((len32 > 4) || !align_start) {
+                       align_end = 4 - (len32 & 3);
+                       len32 += align_end;
+                       if ((rc = bnx2_nvram_read(bp, offset32 + len32 - 4,
+                               end, 4))) {
+                               return rc;
+                       }
+               }
+       }
+
+       if (align_start || align_end) {
+               buf = kmalloc(len32, GFP_KERNEL);
+               if (buf == 0)
+                       return -ENOMEM;
+               if (align_start) {
+                       memcpy(buf, start, 4);
+               }
+               if (align_end) {
+                       memcpy(buf + len32 - 4, end, 4);
+               }
+               memcpy(buf + align_start, data_buf, buf_size);
+       }
+
+       written = 0;
+       while ((written < len32) && (rc == 0)) {
+               u32 page_start, page_end, data_start, data_end;
+               u32 addr, cmd_flags;
+               int i;
+               u8 flash_buffer[264];
+
+               /* Find the page_start addr */
+               page_start = offset32 + written;
+               page_start -= (page_start % bp->flash_info->page_size);
+               /* Find the page_end addr */
+               page_end = page_start + bp->flash_info->page_size;
+               /* Find the data_start addr */
+               data_start = (written == 0) ? offset32 : page_start;
+               /* Find the data_end addr */
+               data_end = (page_end > offset32 + len32) ? 
+                       (offset32 + len32) : page_end;
+
+               /* Request access to the flash interface. */
+               if ((rc = bnx2_acquire_nvram_lock(bp)) != 0)
+                       goto nvram_write_end;
+
+               /* Enable access to flash interface */
+               bnx2_enable_nvram_access(bp);
+
+               cmd_flags = BNX2_NVM_COMMAND_FIRST;
+               if (bp->flash_info->buffered == 0) {
+                       int j;
+
+                       /* Read the whole page into the buffer
+                        * (non-buffer flash only) */
+                       for (j = 0; j < bp->flash_info->page_size; j += 4) {
+                               if (j == (bp->flash_info->page_size - 4)) {
+                                       cmd_flags |= BNX2_NVM_COMMAND_LAST;
+                               }
+                               rc = bnx2_nvram_read_dword(bp,
+                                       page_start + j, 
+                                       &flash_buffer[j], 
+                                       cmd_flags);
+
+                               if (rc)
+                                       goto nvram_write_end;
+
+                               cmd_flags = 0;
+                       }
+               }
+
+               /* Enable writes to flash interface (unlock write-protect) */
+               if ((rc = bnx2_enable_nvram_write(bp)) != 0)
+                       goto nvram_write_end;
+
+               /* Erase the page */
+               if ((rc = bnx2_nvram_erase_page(bp, page_start)) != 0)
+                       goto nvram_write_end;
+
+               /* Re-enable the write again for the actual write */
+               bnx2_enable_nvram_write(bp);
+
+               /* Loop to write back the buffer data from page_start to
+                * data_start */
+               i = 0;
+               if (bp->flash_info->buffered == 0) {
+                       for (addr = page_start; addr < data_start;
+                               addr += 4, i += 4) {
+                               
+                               rc = bnx2_nvram_write_dword(bp, addr,
+                                       &flash_buffer[i], cmd_flags);
+
+                               if (rc != 0)
+                                       goto nvram_write_end;
+
+                               cmd_flags = 0;
+                       }
+               }
+
+               /* Loop to write the new data from data_start to data_end */
+               for (addr = data_start; addr < data_end; addr += 4, i++) {
+                       if ((addr == page_end - 4) ||
+                               ((bp->flash_info->buffered) &&
+                                (addr == data_end - 4))) {
+
+                               cmd_flags |= BNX2_NVM_COMMAND_LAST;
+                       }
+                       rc = bnx2_nvram_write_dword(bp, addr, buf,
+                               cmd_flags);
+
+                       if (rc != 0)
+                               goto nvram_write_end;
+
+                       cmd_flags = 0;
+                       buf += 4;
+               }
+
+               /* Loop to write back the buffer data from data_end
+                * to page_end */
+               if (bp->flash_info->buffered == 0) {
+                       for (addr = data_end; addr < page_end;
+                               addr += 4, i += 4) {
+                       
+                               if (addr == page_end-4) {
+                                       cmd_flags = BNX2_NVM_COMMAND_LAST;
+                               }
+                               rc = bnx2_nvram_write_dword(bp, addr,
+                                       &flash_buffer[i], cmd_flags);
+
+                               if (rc != 0)
+                                       goto nvram_write_end;
+
+                               cmd_flags = 0;
+                       }
+               }
+
+               /* Disable writes to flash interface (lock write-protect) */
+               bnx2_disable_nvram_write(bp);
+
+               /* Disable access to flash interface */
+               bnx2_disable_nvram_access(bp);
+               bnx2_release_nvram_lock(bp);
+
+               /* Increment written */
+               written += data_end - data_start;
+       }
+
+nvram_write_end:
+       if (align_start || align_end)
+               kfree(buf);
+       return rc;
+}
+
+static int
+bnx2_reset_chip(struct bnx2 *bp, u32 reset_code)
+{
+       u32 val;
+       int i, rc = 0;
+
+       /* Wait for the current PCI transaction to complete before
+        * issuing a reset. */
+       REG_WR(bp, BNX2_MISC_ENABLE_CLR_BITS,
+              BNX2_MISC_ENABLE_CLR_BITS_TX_DMA_ENABLE |
+              BNX2_MISC_ENABLE_CLR_BITS_DMA_ENGINE_ENABLE |
+              BNX2_MISC_ENABLE_CLR_BITS_RX_DMA_ENABLE |
+              BNX2_MISC_ENABLE_CLR_BITS_HOST_COALESCE_ENABLE);
+       val = REG_RD(bp, BNX2_MISC_ENABLE_CLR_BITS);
+       udelay(5);
+
+       /* Deposit a driver reset signature so the firmware knows that
+        * this is a soft reset. */
+       REG_WR_IND(bp, HOST_VIEW_SHMEM_BASE + BNX2_DRV_RESET_SIGNATURE,
+                  BNX2_DRV_RESET_SIGNATURE_MAGIC);
+
+       bp->fw_timed_out = 0;
+
+       /* Wait for the firmware to tell us it is ok to issue a reset. */
+       bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT0 | reset_code);
+
+       /* Do a dummy read to force the chip to complete all current transaction
+        * before we issue a reset. */
+       val = REG_RD(bp, BNX2_MISC_ID);
+
+       val = BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ |
+             BNX2_PCICFG_MISC_CONFIG_REG_WINDOW_ENA |
+             BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP;
+
+       /* Chip reset. */
+       REG_WR(bp, BNX2_PCICFG_MISC_CONFIG, val);
+
+       if ((CHIP_ID(bp) == CHIP_ID_5706_A0) ||
+           (CHIP_ID(bp) == CHIP_ID_5706_A1))
+               msleep(15);
+
+       /* Reset takes approximate 30 usec */
+       for (i = 0; i < 10; i++) {
+               val = REG_RD(bp, BNX2_PCICFG_MISC_CONFIG);
+               if ((val & (BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ |
+                           BNX2_PCICFG_MISC_CONFIG_CORE_RST_BSY)) == 0) {
+                       break;
+               }
+               udelay(10);
+       }
+
+       if (val & (BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ |
+                  BNX2_PCICFG_MISC_CONFIG_CORE_RST_BSY)) {
+               printk(KERN_ERR PFX "Chip reset did not complete\n");
+               return -EBUSY;
+       }
+
+       /* Make sure byte swapping is properly configured. */
+       val = REG_RD(bp, BNX2_PCI_SWAP_DIAG0);
+       if (val != 0x01020304) {
+               printk(KERN_ERR PFX "Chip not in correct endian mode\n");
+               return -ENODEV;
+       }
+
+       bp->fw_timed_out = 0;
+
+       /* Wait for the firmware to finish its initialization. */
+       bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT1 | reset_code);
+
+       if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
+               /* Adjust the voltage regular to two steps lower.  The default
+                * of this register is 0x0000000e. */
+               REG_WR(bp, BNX2_MISC_VREG_CONTROL, 0x000000fa);
+
+               /* Remove bad rbuf memory from the free pool. */
+               rc = bnx2_alloc_bad_rbuf(bp);
+       }
+
+       return rc;
+}
+
+static int
+bnx2_init_chip(struct bnx2 *bp)
+{
+       u32 val;
+
+       /* Make sure the interrupt is not active. */
+       REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, BNX2_PCICFG_INT_ACK_CMD_MASK_INT);
+
+       val = BNX2_DMA_CONFIG_DATA_BYTE_SWAP |
+             BNX2_DMA_CONFIG_DATA_WORD_SWAP |
+#ifdef __BIG_ENDIAN
+             BNX2_DMA_CONFIG_CNTL_BYTE_SWAP | 
+#endif
+             BNX2_DMA_CONFIG_CNTL_WORD_SWAP | 
+             DMA_READ_CHANS << 12 |
+             DMA_WRITE_CHANS << 16;
+
+       val |= (0x2 << 20) | (1 << 11);
+
+       if ((bp->flags & PCIX_FLAG) && (bp->bus_speed_mhz = 133))
+               val |= (1 << 23);
+
+       if ((CHIP_NUM(bp) == CHIP_NUM_5706) &&
+           (CHIP_ID(bp) != CHIP_ID_5706_A0) && !(bp->flags & PCIX_FLAG))
+               val |= BNX2_DMA_CONFIG_CNTL_PING_PONG_DMA;
+
+       REG_WR(bp, BNX2_DMA_CONFIG, val);
+
+       if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
+               val = REG_RD(bp, BNX2_TDMA_CONFIG);
+               val |= BNX2_TDMA_CONFIG_ONE_DMA;
+               REG_WR(bp, BNX2_TDMA_CONFIG, val);
+       }
+
+       if (bp->flags & PCIX_FLAG) {
+               u16 val16;
+
+               pci_read_config_word(bp->pdev, bp->pcix_cap + PCI_X_CMD,
+                                    &val16);
+               pci_write_config_word(bp->pdev, bp->pcix_cap + PCI_X_CMD,
+                                     val16 & ~PCI_X_CMD_ERO);
+       }
+
+       REG_WR(bp, BNX2_MISC_ENABLE_SET_BITS,
+              BNX2_MISC_ENABLE_SET_BITS_HOST_COALESCE_ENABLE |
+              BNX2_MISC_ENABLE_STATUS_BITS_RX_V2P_ENABLE |
+              BNX2_MISC_ENABLE_STATUS_BITS_CONTEXT_ENABLE);
+
+       /* Initialize context mapping and zero out the quick contexts.  The
+        * context block must have already been enabled. */
+       bnx2_init_context(bp);
+
+       bnx2_init_cpus(bp);
+       bnx2_init_nvram(bp);
+
+       bnx2_set_mac_addr(bp);
+
+       val = REG_RD(bp, BNX2_MQ_CONFIG);
+       val &= ~BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE;
+       val |= BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_256;
+       REG_WR(bp, BNX2_MQ_CONFIG, val);
+
+       val = 0x10000 + (MAX_CID_CNT * MB_KERNEL_CTX_SIZE);
+       REG_WR(bp, BNX2_MQ_KNL_BYP_WIND_START, val);
+       REG_WR(bp, BNX2_MQ_KNL_WIND_END, val);
+
+       val = (BCM_PAGE_BITS - 8) << 24;
+       REG_WR(bp, BNX2_RV2P_CONFIG, val);
+
+       /* Configure page size. */
+       val = REG_RD(bp, BNX2_TBDR_CONFIG);
+       val &= ~BNX2_TBDR_CONFIG_PAGE_SIZE;
+       val |= (BCM_PAGE_BITS - 8) << 24 | 0x40;
+       REG_WR(bp, BNX2_TBDR_CONFIG, val);
+
+       val = bp->mac_addr[0] +
+             (bp->mac_addr[1] << 8) +
+             (bp->mac_addr[2] << 16) +
+             bp->mac_addr[3] +
+             (bp->mac_addr[4] << 8) +
+             (bp->mac_addr[5] << 16);
+       REG_WR(bp, BNX2_EMAC_BACKOFF_SEED, val);
+
+       /* Program the MTU.  Also include 4 bytes for CRC32. */
+       val = bp->dev->mtu + ETH_HLEN + 4;
+       if (val > (MAX_ETHERNET_PACKET_SIZE + 4))
+               val |= BNX2_EMAC_RX_MTU_SIZE_JUMBO_ENA;
+       REG_WR(bp, BNX2_EMAC_RX_MTU_SIZE, val);
+
+       bp->last_status_idx = 0;
+       bp->rx_mode = BNX2_EMAC_RX_MODE_SORT_MODE;
+
+       /* Set up how to generate a link change interrupt. */
+       REG_WR(bp, BNX2_EMAC_ATTENTION_ENA, BNX2_EMAC_ATTENTION_ENA_LINK);
+
+       REG_WR(bp, BNX2_HC_STATUS_ADDR_L,
+              (u64) bp->status_blk_mapping & 0xffffffff);
+       REG_WR(bp, BNX2_HC_STATUS_ADDR_H, (u64) bp->status_blk_mapping >> 32);
+
+       REG_WR(bp, BNX2_HC_STATISTICS_ADDR_L,
+              (u64) bp->stats_blk_mapping & 0xffffffff);
+       REG_WR(bp, BNX2_HC_STATISTICS_ADDR_H,
+              (u64) bp->stats_blk_mapping >> 32);
+
+       REG_WR(bp, BNX2_HC_TX_QUICK_CONS_TRIP, 
+              (bp->tx_quick_cons_trip_int << 16) | bp->tx_quick_cons_trip);
+
+       REG_WR(bp, BNX2_HC_RX_QUICK_CONS_TRIP,
+              (bp->rx_quick_cons_trip_int << 16) | bp->rx_quick_cons_trip);
+
+       REG_WR(bp, BNX2_HC_COMP_PROD_TRIP,
+              (bp->comp_prod_trip_int << 16) | bp->comp_prod_trip);
+
+       REG_WR(bp, BNX2_HC_TX_TICKS, (bp->tx_ticks_int << 16) | bp->tx_ticks);
+
+       REG_WR(bp, BNX2_HC_RX_TICKS, (bp->rx_ticks_int << 16) | bp->rx_ticks);
+
+       REG_WR(bp, BNX2_HC_COM_TICKS,
+              (bp->com_ticks_int << 16) | bp->com_ticks);
+
+       REG_WR(bp, BNX2_HC_CMD_TICKS,
+              (bp->cmd_ticks_int << 16) | bp->cmd_ticks);
+
+       REG_WR(bp, BNX2_HC_STATS_TICKS, bp->stats_ticks & 0xffff00);
+       REG_WR(bp, BNX2_HC_STAT_COLLECT_TICKS, 0xbb8);  /* 3ms */
+
+       if (CHIP_ID(bp) == CHIP_ID_5706_A1)
+               REG_WR(bp, BNX2_HC_CONFIG, BNX2_HC_CONFIG_COLLECT_STATS);
+       else {
+               REG_WR(bp, BNX2_HC_CONFIG, BNX2_HC_CONFIG_RX_TMR_MODE |
+                      BNX2_HC_CONFIG_TX_TMR_MODE |
+                      BNX2_HC_CONFIG_COLLECT_STATS);
+       }
+
+       /* Clear internal stats counters. */
+       REG_WR(bp, BNX2_HC_COMMAND, BNX2_HC_COMMAND_CLR_STAT_NOW);
+
+       REG_WR(bp, BNX2_HC_ATTN_BITS_ENABLE, STATUS_ATTN_BITS_LINK_STATE);
+
+       /* Initialize the receive filter. */
+       bnx2_set_rx_mode(bp->dev);
+
+       bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT2 | BNX2_DRV_MSG_CODE_RESET);
+
+       REG_WR(bp, BNX2_MISC_ENABLE_SET_BITS, 0x5ffffff);
+       REG_RD(bp, BNX2_MISC_ENABLE_SET_BITS);
+
+       udelay(20);
+
+       return 0;
+}
+
+
+static void
+bnx2_init_tx_ring(struct bnx2 *bp)
+{
+       struct tx_bd *txbd;
+       u32 val;
+
+       txbd = &bp->tx_desc_ring[MAX_TX_DESC_CNT];
+               
+       txbd->tx_bd_haddr_hi = (u64) bp->tx_desc_mapping >> 32;
+       txbd->tx_bd_haddr_lo = (u64) bp->tx_desc_mapping & 0xffffffff;
+
+       bp->tx_prod = 0;
+       bp->tx_cons = 0;
+       bp->tx_prod_bseq = 0;
+       atomic_set(&bp->tx_avail_bd, bp->tx_ring_size);
+       
+       val = BNX2_L2CTX_TYPE_TYPE_L2;
+       val |= BNX2_L2CTX_TYPE_SIZE_L2;
+       CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_TYPE, val);
+
+       val = BNX2_L2CTX_CMD_TYPE_TYPE_L2;
+       val |= 8 << 16;
+       CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_CMD_TYPE, val);
+
+       val = (u64) bp->tx_desc_mapping >> 32;
+       CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_TBDR_BHADDR_HI, val);
+
+       val = (u64) bp->tx_desc_mapping & 0xffffffff;
+       CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_TBDR_BHADDR_LO, val);
+}
+
+static void
+bnx2_init_rx_ring(struct bnx2 *bp)
+{
+       struct rx_bd *rxbd;
+       int i;
+       u16 prod, ring_prod; 
+       u32 val;
+
+       /* 8 for CRC and VLAN */
+       bp->rx_buf_use_size = bp->dev->mtu + ETH_HLEN + bp->rx_offset + 8;
+       /* 8 for alignment */
+       bp->rx_buf_size = bp->rx_buf_use_size + 8;
+
+       ring_prod = prod = bp->rx_prod = 0;
+       bp->rx_cons = 0;
+       bp->rx_prod_bseq = 0;
+               
+       rxbd = &bp->rx_desc_ring[0];
+       for (i = 0; i < MAX_RX_DESC_CNT; i++, rxbd++) {
+               rxbd->rx_bd_len = bp->rx_buf_use_size;
+               rxbd->rx_bd_flags = RX_BD_FLAGS_START | RX_BD_FLAGS_END;
+       }
+
+       rxbd->rx_bd_haddr_hi = (u64) bp->rx_desc_mapping >> 32;
+       rxbd->rx_bd_haddr_lo = (u64) bp->rx_desc_mapping & 0xffffffff;
+
+       val = BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE;
+       val |= BNX2_L2CTX_CTX_TYPE_SIZE_L2;
+       val |= 0x02 << 8;
+       CTX_WR(bp, GET_CID_ADDR(RX_CID), BNX2_L2CTX_CTX_TYPE, val);
+
+       val = (u64) bp->rx_desc_mapping >> 32;
+       CTX_WR(bp, GET_CID_ADDR(RX_CID), BNX2_L2CTX_NX_BDHADDR_HI, val);
+
+       val = (u64) bp->rx_desc_mapping & 0xffffffff;
+       CTX_WR(bp, GET_CID_ADDR(RX_CID), BNX2_L2CTX_NX_BDHADDR_LO, val);
+
+       for ( ;ring_prod < bp->rx_ring_size; ) {
+               if (bnx2_alloc_rx_skb(bp, ring_prod) < 0) {
+                       break;
+               }
+               prod = NEXT_RX_BD(prod);
+               ring_prod = RX_RING_IDX(prod);
+       }
+       bp->rx_prod = prod;
+
+       REG_WR16(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BDIDX, prod);
+
+       REG_WR(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BSEQ, bp->rx_prod_bseq);
+}
+
+static void
+bnx2_free_tx_skbs(struct bnx2 *bp)
+{
+       int i;
+
+       if (bp->tx_buf_ring == NULL)
+               return;
+
+       for (i = 0; i < TX_DESC_CNT; ) {
+               struct sw_bd *tx_buf = &bp->tx_buf_ring[i];
+               struct sk_buff *skb = tx_buf->skb;
+               int j, last;
+
+               if (skb == NULL) {
+                       i++;
+                       continue;
+               }
+
+               pci_unmap_single(bp->pdev, pci_unmap_addr(tx_buf, mapping),
+                       skb_headlen(skb), PCI_DMA_TODEVICE);
+
+               tx_buf->skb = NULL;
+
+               last = skb_shinfo(skb)->nr_frags;
+               for (j = 0; j < last; j++) {
+                       tx_buf = &bp->tx_buf_ring[i + j + 1];
+                       pci_unmap_page(bp->pdev,
+                               pci_unmap_addr(tx_buf, mapping),
+                               skb_shinfo(skb)->frags[j].size,
+                               PCI_DMA_TODEVICE);
+               }
+               dev_kfree_skb_any(skb);
+               i += j + 1;
+       }
+
+}
+
+static void
+bnx2_free_rx_skbs(struct bnx2 *bp)
+{
+       int i;
+
+       if (bp->rx_buf_ring == NULL)
+               return;
+
+       for (i = 0; i < RX_DESC_CNT; i++) {
+               struct sw_bd *rx_buf = &bp->rx_buf_ring[i];
+               struct sk_buff *skb = rx_buf->skb;
+
+               if (skb == 0)
+                       continue;
+
+               pci_unmap_single(bp->pdev, pci_unmap_addr(rx_buf, mapping),
+                       bp->rx_buf_use_size, PCI_DMA_FROMDEVICE);
+
+               rx_buf->skb = NULL;
+
+               dev_kfree_skb_any(skb);
+       }
+}
+
+static void
+bnx2_free_skbs(struct bnx2 *bp)
+{
+       bnx2_free_tx_skbs(bp);
+       bnx2_free_rx_skbs(bp);
+}
+
+static int
+bnx2_reset_nic(struct bnx2 *bp, u32 reset_code)
+{
+       int rc;
+
+       rc = bnx2_reset_chip(bp, reset_code);
+       bnx2_free_skbs(bp);
+       if (rc)
+               return rc;
+
+       bnx2_init_chip(bp);
+       bnx2_init_tx_ring(bp);
+       bnx2_init_rx_ring(bp);
+       return 0;
+}
+
+static int
+bnx2_init_nic(struct bnx2 *bp)
+{
+       int rc;
+
+       if ((rc = bnx2_reset_nic(bp, BNX2_DRV_MSG_CODE_RESET)) != 0)
+               return rc;
+
+       bnx2_init_phy(bp);
+       bnx2_set_link(bp);
+       return 0;
+}
+
+static int
+bnx2_test_registers(struct bnx2 *bp)
+{
+       int ret;
+       int i;
+       static struct {
+               u16   offset;
+               u16   flags;
+               u32   rw_mask;
+               u32   ro_mask;
+       } reg_tbl[] = {
+               { 0x006c, 0, 0x00000000, 0x0000003f },
+               { 0x0090, 0, 0xffffffff, 0x00000000 },
+               { 0x0094, 0, 0x00000000, 0x00000000 },
+
+               { 0x0404, 0, 0x00003f00, 0x00000000 },
+               { 0x0418, 0, 0x00000000, 0xffffffff },
+               { 0x041c, 0, 0x00000000, 0xffffffff },
+               { 0x0420, 0, 0x00000000, 0x80ffffff },
+               { 0x0424, 0, 0x00000000, 0x00000000 },
+               { 0x0428, 0, 0x00000000, 0x00000001 },
+               { 0x0450, 0, 0x00000000, 0x0000ffff },
+               { 0x0454, 0, 0x00000000, 0xffffffff },
+               { 0x0458, 0, 0x00000000, 0xffffffff },
+
+               { 0x0808, 0, 0x00000000, 0xffffffff },
+               { 0x0854, 0, 0x00000000, 0xffffffff },
+               { 0x0868, 0, 0x00000000, 0x77777777 },
+               { 0x086c, 0, 0x00000000, 0x77777777 },
+               { 0x0870, 0, 0x00000000, 0x77777777 },
+               { 0x0874, 0, 0x00000000, 0x77777777 },
+
+               { 0x0c00, 0, 0x00000000, 0x00000001 },
+               { 0x0c04, 0, 0x00000000, 0x03ff0001 },
+               { 0x0c08, 0, 0x0f0ff073, 0x00000000 },
+               { 0x0c0c, 0, 0x00ffffff, 0x00000000 },
+               { 0x0c30, 0, 0x00000000, 0xffffffff },
+               { 0x0c34, 0, 0x00000000, 0xffffffff },
+               { 0x0c38, 0, 0x00000000, 0xffffffff },
+               { 0x0c3c, 0, 0x00000000, 0xffffffff },
+               { 0x0c40, 0, 0x00000000, 0xffffffff },
+               { 0x0c44, 0, 0x00000000, 0xffffffff },
+               { 0x0c48, 0, 0x00000000, 0x0007ffff },
+               { 0x0c4c, 0, 0x00000000, 0xffffffff },
+               { 0x0c50, 0, 0x00000000, 0xffffffff },
+               { 0x0c54, 0, 0x00000000, 0xffffffff },
+               { 0x0c58, 0, 0x00000000, 0xffffffff },
+               { 0x0c5c, 0, 0x00000000, 0xffffffff },
+               { 0x0c60, 0, 0x00000000, 0xffffffff },
+               { 0x0c64, 0, 0x00000000, 0xffffffff },
+               { 0x0c68, 0, 0x00000000, 0xffffffff },
+               { 0x0c6c, 0, 0x00000000, 0xffffffff },
+               { 0x0c70, 0, 0x00000000, 0xffffffff },
+               { 0x0c74, 0, 0x00000000, 0xffffffff },
+               { 0x0c78, 0, 0x00000000, 0xffffffff },
+               { 0x0c7c, 0, 0x00000000, 0xffffffff },
+               { 0x0c80, 0, 0x00000000, 0xffffffff },
+               { 0x0c84, 0, 0x00000000, 0xffffffff },
+               { 0x0c88, 0, 0x00000000, 0xffffffff },
+               { 0x0c8c, 0, 0x00000000, 0xffffffff },
+               { 0x0c90, 0, 0x00000000, 0xffffffff },
+               { 0x0c94, 0, 0x00000000, 0xffffffff },
+               { 0x0c98, 0, 0x00000000, 0xffffffff },
+               { 0x0c9c, 0, 0x00000000, 0xffffffff },
+               { 0x0ca0, 0, 0x00000000, 0xffffffff },
+               { 0x0ca4, 0, 0x00000000, 0xffffffff },
+               { 0x0ca8, 0, 0x00000000, 0x0007ffff },
+               { 0x0cac, 0, 0x00000000, 0xffffffff },
+               { 0x0cb0, 0, 0x00000000, 0xffffffff },
+               { 0x0cb4, 0, 0x00000000, 0xffffffff },
+               { 0x0cb8, 0, 0x00000000, 0xffffffff },
+               { 0x0cbc, 0, 0x00000000, 0xffffffff },
+               { 0x0cc0, 0, 0x00000000, 0xffffffff },
+               { 0x0cc4, 0, 0x00000000, 0xffffffff },
+               { 0x0cc8, 0, 0x00000000, 0xffffffff },
+               { 0x0ccc, 0, 0x00000000, 0xffffffff },
+               { 0x0cd0, 0, 0x00000000, 0xffffffff },
+               { 0x0cd4, 0, 0x00000000, 0xffffffff },
+               { 0x0cd8, 0, 0x00000000, 0xffffffff },
+               { 0x0cdc, 0, 0x00000000, 0xffffffff },
+               { 0x0ce0, 0, 0x00000000, 0xffffffff },
+               { 0x0ce4, 0, 0x00000000, 0xffffffff },
+               { 0x0ce8, 0, 0x00000000, 0xffffffff },
+               { 0x0cec, 0, 0x00000000, 0xffffffff },
+               { 0x0cf0, 0, 0x00000000, 0xffffffff },
+               { 0x0cf4, 0, 0x00000000, 0xffffffff },
+               { 0x0cf8, 0, 0x00000000, 0xffffffff },
+               { 0x0cfc, 0, 0x00000000, 0xffffffff },
+               { 0x0d00, 0, 0x00000000, 0xffffffff },
+               { 0x0d04, 0, 0x00000000, 0xffffffff },
+
+               { 0x1000, 0, 0x00000000, 0x00000001 },
+               { 0x1004, 0, 0x00000000, 0x000f0001 },
+               { 0x1044, 0, 0x00000000, 0xffc003ff },
+               { 0x1080, 0, 0x00000000, 0x0001ffff },
+               { 0x1084, 0, 0x00000000, 0xffffffff },
+               { 0x1088, 0, 0x00000000, 0xffffffff },
+               { 0x108c, 0, 0x00000000, 0xffffffff },
+               { 0x1090, 0, 0x00000000, 0xffffffff },
+               { 0x1094, 0, 0x00000000, 0xffffffff },
+               { 0x1098, 0, 0x00000000, 0xffffffff },
+               { 0x109c, 0, 0x00000000, 0xffffffff },
+               { 0x10a0, 0, 0x00000000, 0xffffffff },
+
+               { 0x1408, 0, 0x01c00800, 0x00000000 },
+               { 0x149c, 0, 0x8000ffff, 0x00000000 },
+               { 0x14a8, 0, 0x00000000, 0x000001ff },
+               { 0x14ac, 0, 0x4fffffff, 0x10000000 },
+               { 0x14b0, 0, 0x00000002, 0x00000001 },
+               { 0x14b8, 0, 0x00000000, 0x00000000 },
+               { 0x14c0, 0, 0x00000000, 0x00000009 },
+               { 0x14c4, 0, 0x00003fff, 0x00000000 },
+               { 0x14cc, 0, 0x00000000, 0x00000001 },
+               { 0x14d0, 0, 0xffffffff, 0x00000000 },
+               { 0x1500, 0, 0x00000000, 0xffffffff },
+               { 0x1504, 0, 0x00000000, 0xffffffff },
+               { 0x1508, 0, 0x00000000, 0xffffffff },
+               { 0x150c, 0, 0x00000000, 0xffffffff },
+               { 0x1510, 0, 0x00000000, 0xffffffff },
+               { 0x1514, 0, 0x00000000, 0xffffffff },
+               { 0x1518, 0, 0x00000000, 0xffffffff },
+               { 0x151c, 0, 0x00000000, 0xffffffff },
+               { 0x1520, 0, 0x00000000, 0xffffffff },
+               { 0x1524, 0, 0x00000000, 0xffffffff },
+               { 0x1528, 0, 0x00000000, 0xffffffff },
+               { 0x152c, 0, 0x00000000, 0xffffffff },
+               { 0x1530, 0, 0x00000000, 0xffffffff },
+               { 0x1534, 0, 0x00000000, 0xffffffff },
+               { 0x1538, 0, 0x00000000, 0xffffffff },
+               { 0x153c, 0, 0x00000000, 0xffffffff },
+               { 0x1540, 0, 0x00000000, 0xffffffff },
+               { 0x1544, 0, 0x00000000, 0xffffffff },
+               { 0x1548, 0, 0x00000000, 0xffffffff },
+               { 0x154c, 0, 0x00000000, 0xffffffff },
+               { 0x1550, 0, 0x00000000, 0xffffffff },
+               { 0x1554, 0, 0x00000000, 0xffffffff },
+               { 0x1558, 0, 0x00000000, 0xffffffff },
+               { 0x1600, 0, 0x00000000, 0xffffffff },
+               { 0x1604, 0, 0x00000000, 0xffffffff },
+               { 0x1608, 0, 0x00000000, 0xffffffff },
+               { 0x160c, 0, 0x00000000, 0xffffffff },
+               { 0x1610, 0, 0x00000000, 0xffffffff },
+               { 0x1614, 0, 0x00000000, 0xffffffff },
+               { 0x1618, 0, 0x00000000, 0xffffffff },
+               { 0x161c, 0, 0x00000000, 0xffffffff },
+               { 0x1620, 0, 0x00000000, 0xffffffff },
+               { 0x1624, 0, 0x00000000, 0xffffffff },
+               { 0x1628, 0, 0x00000000, 0xffffffff },
+               { 0x162c, 0, 0x00000000, 0xffffffff },
+               { 0x1630, 0, 0x00000000, 0xffffffff },
+               { 0x1634, 0, 0x00000000, 0xffffffff },
+               { 0x1638, 0, 0x00000000, 0xffffffff },
+               { 0x163c, 0, 0x00000000, 0xffffffff },
+               { 0x1640, 0, 0x00000000, 0xffffffff },
+               { 0x1644, 0, 0x00000000, 0xffffffff },
+               { 0x1648, 0, 0x00000000, 0xffffffff },
+               { 0x164c, 0, 0x00000000, 0xffffffff },
+               { 0x1650, 0, 0x00000000, 0xffffffff },
+               { 0x1654, 0, 0x00000000, 0xffffffff },
+
+               { 0x1800, 0, 0x00000000, 0x00000001 },
+               { 0x1804, 0, 0x00000000, 0x00000003 },
+               { 0x1840, 0, 0x00000000, 0xffffffff },
+               { 0x1844, 0, 0x00000000, 0xffffffff },
+               { 0x1848, 0, 0x00000000, 0xffffffff },
+               { 0x184c, 0, 0x00000000, 0xffffffff },
+               { 0x1850, 0, 0x00000000, 0xffffffff },
+               { 0x1900, 0, 0x7ffbffff, 0x00000000 },
+               { 0x1904, 0, 0xffffffff, 0x00000000 },
+               { 0x190c, 0, 0xffffffff, 0x00000000 },
+               { 0x1914, 0, 0xffffffff, 0x00000000 },
+               { 0x191c, 0, 0xffffffff, 0x00000000 },
+               { 0x1924, 0, 0xffffffff, 0x00000000 },
+               { 0x192c, 0, 0xffffffff, 0x00000000 },
+               { 0x1934, 0, 0xffffffff, 0x00000000 },
+               { 0x193c, 0, 0xffffffff, 0x00000000 },
+               { 0x1944, 0, 0xffffffff, 0x00000000 },
+               { 0x194c, 0, 0xffffffff, 0x00000000 },
+               { 0x1954, 0, 0xffffffff, 0x00000000 },
+               { 0x195c, 0, 0xffffffff, 0x00000000 },
+               { 0x1964, 0, 0xffffffff, 0x00000000 },
+               { 0x196c, 0, 0xffffffff, 0x00000000 },
+               { 0x1974, 0, 0xffffffff, 0x00000000 },
+               { 0x197c, 0, 0xffffffff, 0x00000000 },
+               { 0x1980, 0, 0x0700ffff, 0x00000000 },
+
+               { 0x1c00, 0, 0x00000000, 0x00000001 },
+               { 0x1c04, 0, 0x00000000, 0x00000003 },
+               { 0x1c08, 0, 0x0000000f, 0x00000000 },
+               { 0x1c40, 0, 0x00000000, 0xffffffff },
+               { 0x1c44, 0, 0x00000000, 0xffffffff },
+               { 0x1c48, 0, 0x00000000, 0xffffffff },
+               { 0x1c4c, 0, 0x00000000, 0xffffffff },
+               { 0x1c50, 0, 0x00000000, 0xffffffff },
+               { 0x1d00, 0, 0x7ffbffff, 0x00000000 },
+               { 0x1d04, 0, 0xffffffff, 0x00000000 },
+               { 0x1d0c, 0, 0xffffffff, 0x00000000 },
+               { 0x1d14, 0, 0xffffffff, 0x00000000 },
+               { 0x1d1c, 0, 0xffffffff, 0x00000000 },
+               { 0x1d24, 0, 0xffffffff, 0x00000000 },
+               { 0x1d2c, 0, 0xffffffff, 0x00000000 },
+               { 0x1d34, 0, 0xffffffff, 0x00000000 },
+               { 0x1d3c, 0, 0xffffffff, 0x00000000 },
+               { 0x1d44, 0, 0xffffffff, 0x00000000 },
+               { 0x1d4c, 0, 0xffffffff, 0x00000000 },
+               { 0x1d54, 0, 0xffffffff, 0x00000000 },
+               { 0x1d5c, 0, 0xffffffff, 0x00000000 },
+               { 0x1d64, 0, 0xffffffff, 0x00000000 },
+               { 0x1d6c, 0, 0xffffffff, 0x00000000 },
+               { 0x1d74, 0, 0xffffffff, 0x00000000 },
+               { 0x1d7c, 0, 0xffffffff, 0x00000000 },
+               { 0x1d80, 0, 0x0700ffff, 0x00000000 },
+
+               { 0x2004, 0, 0x00000000, 0x0337000f },
+               { 0x2008, 0, 0xffffffff, 0x00000000 },
+               { 0x200c, 0, 0xffffffff, 0x00000000 },
+               { 0x2010, 0, 0xffffffff, 0x00000000 },
+               { 0x2014, 0, 0x801fff80, 0x00000000 },
+               { 0x2018, 0, 0x000003ff, 0x00000000 },
+
+               { 0x2800, 0, 0x00000000, 0x00000001 },
+               { 0x2804, 0, 0x00000000, 0x00003f01 },
+               { 0x2808, 0, 0x0f3f3f03, 0x00000000 },
+               { 0x2810, 0, 0xffff0000, 0x00000000 },
+               { 0x2814, 0, 0xffff0000, 0x00000000 },
+               { 0x2818, 0, 0xffff0000, 0x00000000 },
+               { 0x281c, 0, 0xffff0000, 0x00000000 },
+               { 0x2834, 0, 0xffffffff, 0x00000000 },
+               { 0x2840, 0, 0x00000000, 0xffffffff },
+               { 0x2844, 0, 0x00000000, 0xffffffff },
+               { 0x2848, 0, 0xffffffff, 0x00000000 },
+               { 0x284c, 0, 0xf800f800, 0x07ff07ff },
+
+               { 0x2c00, 0, 0x00000000, 0x00000011 },
+               { 0x2c04, 0, 0x00000000, 0x00030007 },
+
+               { 0x3000, 0, 0x00000000, 0x00000001 },
+               { 0x3004, 0, 0x00000000, 0x007007ff },
+               { 0x3008, 0, 0x00000003, 0x00000000 },
+               { 0x300c, 0, 0xffffffff, 0x00000000 },
+               { 0x3010, 0, 0xffffffff, 0x00000000 },
+               { 0x3014, 0, 0xffffffff, 0x00000000 },
+               { 0x3034, 0, 0xffffffff, 0x00000000 },
+               { 0x3038, 0, 0xffffffff, 0x00000000 },
+               { 0x3050, 0, 0x00000001, 0x00000000 },
+
+               { 0x3c00, 0, 0x00000000, 0x00000001 },
+               { 0x3c04, 0, 0x00000000, 0x00070000 },
+               { 0x3c08, 0, 0x00007f71, 0x07f00000 },
+               { 0x3c0c, 0, 0x1f3ffffc, 0x00000000 },
+               { 0x3c10, 0, 0xffffffff, 0x00000000 },
+               { 0x3c14, 0, 0x00000000, 0xffffffff },
+               { 0x3c18, 0, 0x00000000, 0xffffffff },
+               { 0x3c1c, 0, 0xfffff000, 0x00000000 },
+               { 0x3c20, 0, 0xffffff00, 0x00000000 },
+               { 0x3c24, 0, 0xffffffff, 0x00000000 },
+               { 0x3c28, 0, 0xffffffff, 0x00000000 },
+               { 0x3c2c, 0, 0xffffffff, 0x00000000 },
+               { 0x3c30, 0, 0xffffffff, 0x00000000 },
+               { 0x3c34, 0, 0xffffffff, 0x00000000 },
+               { 0x3c38, 0, 0xffffffff, 0x00000000 },
+               { 0x3c3c, 0, 0xffffffff, 0x00000000 },
+               { 0x3c40, 0, 0xffffffff, 0x00000000 },
+               { 0x3c44, 0, 0xffffffff, 0x00000000 },
+               { 0x3c48, 0, 0xffffffff, 0x00000000 },
+               { 0x3c4c, 0, 0xffffffff, 0x00000000 },
+               { 0x3c50, 0, 0xffffffff, 0x00000000 },
+               { 0x3c54, 0, 0xffffffff, 0x00000000 },
+               { 0x3c58, 0, 0xffffffff, 0x00000000 },
+               { 0x3c5c, 0, 0xffffffff, 0x00000000 },
+               { 0x3c60, 0, 0xffffffff, 0x00000000 },
+               { 0x3c64, 0, 0xffffffff, 0x00000000 },
+               { 0x3c68, 0, 0xffffffff, 0x00000000 },
+               { 0x3c6c, 0, 0xffffffff, 0x00000000 },
+               { 0x3c70, 0, 0xffffffff, 0x00000000 },
+               { 0x3c74, 0, 0x0000003f, 0x00000000 },
+               { 0x3c78, 0, 0x00000000, 0x00000000 },
+               { 0x3c7c, 0, 0x00000000, 0x00000000 },
+               { 0x3c80, 0, 0x3fffffff, 0x00000000 },
+               { 0x3c84, 0, 0x0000003f, 0x00000000 },
+               { 0x3c88, 0, 0x00000000, 0xffffffff },
+               { 0x3c8c, 0, 0x00000000, 0xffffffff },
+
+               { 0x4000, 0, 0x00000000, 0x00000001 },
+               { 0x4004, 0, 0x00000000, 0x00030000 },
+               { 0x4008, 0, 0x00000ff0, 0x00000000 },
+               { 0x400c, 0, 0xffffffff, 0x00000000 },
+               { 0x4088, 0, 0x00000000, 0x00070303 },
+
+               { 0x4400, 0, 0x00000000, 0x00000001 },
+               { 0x4404, 0, 0x00000000, 0x00003f01 },
+               { 0x4408, 0, 0x7fff00ff, 0x00000000 },
+               { 0x440c, 0, 0xffffffff, 0x00000000 },
+               { 0x4410, 0, 0xffff,     0x0000 },
+               { 0x4414, 0, 0xffff,     0x0000 },
+               { 0x4418, 0, 0xffff,     0x0000 },
+               { 0x441c, 0, 0xffff,     0x0000 },
+               { 0x4428, 0, 0xffffffff, 0x00000000 },
+               { 0x442c, 0, 0xffffffff, 0x00000000 },
+               { 0x4430, 0, 0xffffffff, 0x00000000 },
+               { 0x4434, 0, 0xffffffff, 0x00000000 },
+               { 0x4438, 0, 0xffffffff, 0x00000000 },
+               { 0x443c, 0, 0xffffffff, 0x00000000 },
+               { 0x4440, 0, 0xffffffff, 0x00000000 },
+               { 0x4444, 0, 0xffffffff, 0x00000000 },
+
+               { 0x4c00, 0, 0x00000000, 0x00000001 },
+               { 0x4c04, 0, 0x00000000, 0x0000003f },
+               { 0x4c08, 0, 0xffffffff, 0x00000000 },
+               { 0x4c0c, 0, 0x0007fc00, 0x00000000 },
+               { 0x4c10, 0, 0x80003fe0, 0x00000000 },
+               { 0x4c14, 0, 0xffffffff, 0x00000000 },
+               { 0x4c44, 0, 0x00000000, 0x9fff9fff },
+               { 0x4c48, 0, 0x00000000, 0xb3009fff },
+               { 0x4c4c, 0, 0x00000000, 0x77f33b30 },
+               { 0x4c50, 0, 0x00000000, 0xffffffff },
+
+               { 0x5004, 0, 0x00000000, 0x0000007f },
+               { 0x5008, 0, 0x0f0007ff, 0x00000000 },
+               { 0x500c, 0, 0xf800f800, 0x07ff07ff },
+
+               { 0x5400, 0, 0x00000008, 0x00000001 },
+               { 0x5404, 0, 0x00000000, 0x0000003f },
+               { 0x5408, 0, 0x0000001f, 0x00000000 },
+               { 0x540c, 0, 0xffffffff, 0x00000000 },
+               { 0x5410, 0, 0xffffffff, 0x00000000 },
+               { 0x5414, 0, 0x0000ffff, 0x00000000 },
+               { 0x5418, 0, 0x0000ffff, 0x00000000 },
+               { 0x541c, 0, 0x0000ffff, 0x00000000 },
+               { 0x5420, 0, 0x0000ffff, 0x00000000 },
+               { 0x5428, 0, 0x000000ff, 0x00000000 },
+               { 0x542c, 0, 0xff00ffff, 0x00000000 },
+               { 0x5430, 0, 0x001fff80, 0x00000000 },
+               { 0x5438, 0, 0xffffffff, 0x00000000 },
+               { 0x543c, 0, 0xffffffff, 0x00000000 },
+               { 0x5440, 0, 0xf800f800, 0x07ff07ff },
+
+               { 0x5c00, 0, 0x00000000, 0x00000001 },
+               { 0x5c04, 0, 0x00000000, 0x0003000f },
+               { 0x5c08, 0, 0x00000003, 0x00000000 },
+               { 0x5c0c, 0, 0x0000fff8, 0x00000000 },
+               { 0x5c10, 0, 0x00000000, 0xffffffff },
+               { 0x5c80, 0, 0x00000000, 0x0f7113f1 },
+               { 0x5c84, 0, 0x00000000, 0x0000f333 },
+               { 0x5c88, 0, 0x00000000, 0x00077373 },
+               { 0x5c8c, 0, 0x00000000, 0x0007f737 },
+
+               { 0x6808, 0, 0x0000ff7f, 0x00000000 },
+               { 0x680c, 0, 0xffffffff, 0x00000000 },
+               { 0x6810, 0, 0xffffffff, 0x00000000 },
+               { 0x6814, 0, 0xffffffff, 0x00000000 },
+               { 0x6818, 0, 0xffffffff, 0x00000000 },
+               { 0x681c, 0, 0xffffffff, 0x00000000 },
+               { 0x6820, 0, 0x00ff00ff, 0x00000000 },
+               { 0x6824, 0, 0x00ff00ff, 0x00000000 },
+               { 0x6828, 0, 0x00ff00ff, 0x00000000 },
+               { 0x682c, 0, 0x03ff03ff, 0x00000000 },
+               { 0x6830, 0, 0x03ff03ff, 0x00000000 },
+               { 0x6834, 0, 0x03ff03ff, 0x00000000 },
+               { 0x6838, 0, 0x03ff03ff, 0x00000000 },
+               { 0x683c, 0, 0x0000ffff, 0x00000000 },
+               { 0x6840, 0, 0x00000ff0, 0x00000000 },
+               { 0x6844, 0, 0x00ffff00, 0x00000000 },
+               { 0x684c, 0, 0xffffffff, 0x00000000 },
+               { 0x6850, 0, 0x7f7f7f7f, 0x00000000 },
+               { 0x6854, 0, 0x7f7f7f7f, 0x00000000 },
+               { 0x6858, 0, 0x7f7f7f7f, 0x00000000 },
+               { 0x685c, 0, 0x7f7f7f7f, 0x00000000 },
+               { 0x6908, 0, 0x00000000, 0x0001ff0f },
+               { 0x690c, 0, 0x00000000, 0x0ffe00f0 },
+
+               { 0xffff, 0, 0x00000000, 0x00000000 },
+       };
+
+       ret = 0;
+       for (i = 0; reg_tbl[i].offset != 0xffff; i++) {
+               u32 offset, rw_mask, ro_mask, save_val, val;
+
+               offset = (u32) reg_tbl[i].offset;
+               rw_mask = reg_tbl[i].rw_mask;
+               ro_mask = reg_tbl[i].ro_mask;
+
+               save_val = readl((u8 *) bp->regview + offset);
+
+               writel(0, (u8 *) bp->regview + offset);
+
+               val = readl((u8 *) bp->regview + offset);
+               if ((val & rw_mask) != 0) {
+                       goto reg_test_err;
+               }
+
+               if ((val & ro_mask) != (save_val & ro_mask)) {
+                       goto reg_test_err;
+               }
+
+               writel(0xffffffff, (u8 *) bp->regview + offset);
+
+               val = readl((u8 *) bp->regview + offset);
+               if ((val & rw_mask) != rw_mask) {
+                       goto reg_test_err;
+               }
+
+               if ((val & ro_mask) != (save_val & ro_mask)) {
+                       goto reg_test_err;
+               }
+
+               writel(save_val, (u8 *) bp->regview + offset);
+               continue;
+
+reg_test_err:
+               writel(save_val, (u8 *) bp->regview + offset);
+               ret = -ENODEV;
+               break;
+       }
+       return ret;
+}
+
+static int
+bnx2_do_mem_test(struct bnx2 *bp, u32 start, u32 size)
+{
+       static u32 test_pattern[] = { 0x00000000, 0xffffffff, 0x55555555,
+               0xaaaaaaaa , 0xaa55aa55, 0x55aa55aa };
+       int i;
+
+       for (i = 0; i < sizeof(test_pattern) / 4; i++) {
+               u32 offset;
+
+               for (offset = 0; offset < size; offset += 4) {
+
+                       REG_WR_IND(bp, start + offset, test_pattern[i]);
+
+                       if (REG_RD_IND(bp, start + offset) !=
+                               test_pattern[i]) {
+                               return -ENODEV;
+                       }
+               }
+       }
+       return 0;
+}
+
+static int
+bnx2_test_memory(struct bnx2 *bp)
+{
+       int ret = 0;
+       int i;
+       static struct {
+               u32   offset;
+               u32   len;
+       } mem_tbl[] = {
+               { 0x60000,  0x4000 },
+               { 0xa0000,  0x4000 },
+               { 0xe0000,  0x4000 },
+               { 0x120000, 0x4000 },
+               { 0x1a0000, 0x4000 },
+               { 0x160000, 0x4000 },
+               { 0xffffffff, 0    },
+       };
+
+       for (i = 0; mem_tbl[i].offset != 0xffffffff; i++) {
+               if ((ret = bnx2_do_mem_test(bp, mem_tbl[i].offset,
+                       mem_tbl[i].len)) != 0) {
+                       return ret;
+               }
+       }
+       
+       return ret;
+}
+
+static int
+bnx2_test_loopback(struct bnx2 *bp)
+{
+       unsigned int pkt_size, num_pkts, i;
+       struct sk_buff *skb, *rx_skb;
+       unsigned char *packet;
+       u16 rx_start_idx, rx_idx, send_idx;
+       u32 send_bseq, val;
+       dma_addr_t map;
+       struct tx_bd *txbd;
+       struct sw_bd *rx_buf;
+       struct l2_fhdr *rx_hdr;
+       int ret = -ENODEV;
+
+       if (!netif_running(bp->dev))
+               return -ENODEV;
+
+       bp->loopback = MAC_LOOPBACK;
+       bnx2_reset_nic(bp, BNX2_DRV_MSG_CODE_DIAG);
+       bnx2_set_mac_loopback(bp);
+
+       pkt_size = 1514;
+       skb = dev_alloc_skb(pkt_size);
+       packet = skb_put(skb, pkt_size);
+       memcpy(packet, bp->mac_addr, 6);
+       memset(packet + 6, 0x0, 8);
+       for (i = 14; i < pkt_size; i++)
+               packet[i] = (unsigned char) (i & 0xff);
+
+       map = pci_map_single(bp->pdev, skb->data, pkt_size,
+               PCI_DMA_TODEVICE);
+
+       val = REG_RD(bp, BNX2_HC_COMMAND);
+       REG_WR(bp, BNX2_HC_COMMAND, val | BNX2_HC_COMMAND_COAL_NOW_WO_INT);
+       REG_RD(bp, BNX2_HC_COMMAND);
+
+       udelay(5);
+       rx_start_idx = bp->status_blk->status_rx_quick_consumer_index0;
+
+       send_idx = 0;
+       send_bseq = 0;
+       num_pkts = 0;
+
+       txbd = &bp->tx_desc_ring[send_idx];
+
+       txbd->tx_bd_haddr_hi = (u64) map >> 32;
+       txbd->tx_bd_haddr_lo = (u64) map & 0xffffffff;
+       txbd->tx_bd_mss_nbytes = pkt_size;
+       txbd->tx_bd_vlan_tag_flags = TX_BD_FLAGS_START | TX_BD_FLAGS_END;
+
+       num_pkts++;
+       send_idx = NEXT_TX_BD(send_idx);
+
+       send_bseq += pkt_size;
+
+       REG_WR16(bp, MB_TX_CID_ADDR + BNX2_L2CTX_TX_HOST_BIDX, send_idx);
+       REG_WR(bp, MB_TX_CID_ADDR + BNX2_L2CTX_TX_HOST_BSEQ, send_bseq);
+
+
+       udelay(100);
+
+       val = REG_RD(bp, BNX2_HC_COMMAND);
+       REG_WR(bp, BNX2_HC_COMMAND, val | BNX2_HC_COMMAND_COAL_NOW_WO_INT);
+       REG_RD(bp, BNX2_HC_COMMAND);
+
+       udelay(5);
+
+       pci_unmap_single(bp->pdev, map, pkt_size, PCI_DMA_TODEVICE);
+       dev_kfree_skb_irq(skb);
+
+       if (bp->status_blk->status_tx_quick_consumer_index0 != send_idx) {
+               goto loopback_test_done;
+       }
+
+       rx_idx = bp->status_blk->status_rx_quick_consumer_index0;
+       if (rx_idx != rx_start_idx + num_pkts) {
+               goto loopback_test_done;
+       }
+
+       rx_buf = &bp->rx_buf_ring[rx_start_idx];
+       rx_skb = rx_buf->skb;
+
+       rx_hdr = (struct l2_fhdr *) rx_skb->data;
+       skb_reserve(rx_skb, bp->rx_offset);
+
+       pci_dma_sync_single_for_cpu(bp->pdev,
+               pci_unmap_addr(rx_buf, mapping),
+               bp->rx_buf_size, PCI_DMA_FROMDEVICE);
+
+       if (rx_hdr->l2_fhdr_errors &
+               (L2_FHDR_ERRORS_BAD_CRC |
+               L2_FHDR_ERRORS_PHY_DECODE |
+               L2_FHDR_ERRORS_ALIGNMENT |
+               L2_FHDR_ERRORS_TOO_SHORT |
+               L2_FHDR_ERRORS_GIANT_FRAME)) {
+
+               goto loopback_test_done;
+       }
+
+       if ((rx_hdr->l2_fhdr_pkt_len - 4) != pkt_size) {
+               goto loopback_test_done;
+       }
+
+       for (i = 14; i < pkt_size; i++) {
+               if (*(rx_skb->data + i) != (unsigned char) (i & 0xff)) {
+                       goto loopback_test_done;
+               }
+       }
+
+       ret = 0;
+
+loopback_test_done:
+       bp->loopback = 0;
+       return ret;
+}
+
+#define NVRAM_SIZE 0x200
+#define CRC32_RESIDUAL 0xdebb20e3
+
+static int
+bnx2_test_nvram(struct bnx2 *bp)
+{
+       u32 buf[NVRAM_SIZE / 4];
+       u8 *data = (u8 *) buf;
+       int rc = 0;
+       u32 magic, csum;
+
+       if ((rc = bnx2_nvram_read(bp, 0, data, 4)) != 0)
+               goto test_nvram_done;
+
+        magic = be32_to_cpu(buf[0]);
+       if (magic != 0x669955aa) {
+               rc = -ENODEV;
+               goto test_nvram_done;
+       }
+
+       if ((rc = bnx2_nvram_read(bp, 0x100, data, NVRAM_SIZE)) != 0)
+               goto test_nvram_done;
+
+       csum = ether_crc_le(0x100, data);
+       if (csum != CRC32_RESIDUAL) {
+               rc = -ENODEV;
+               goto test_nvram_done;
+       }
+
+       csum = ether_crc_le(0x100, data + 0x100);
+       if (csum != CRC32_RESIDUAL) {
+               rc = -ENODEV;
+       }
+
+test_nvram_done:
+       return rc;
+}
+
+static int
+bnx2_test_link(struct bnx2 *bp)
+{
+       u32 bmsr;
+
+       spin_lock_irq(&bp->phy_lock);
+       bnx2_read_phy(bp, MII_BMSR, &bmsr);
+       bnx2_read_phy(bp, MII_BMSR, &bmsr);
+       spin_unlock_irq(&bp->phy_lock);
+               
+       if (bmsr & BMSR_LSTATUS) {
+               return 0;
+       }
+       return -ENODEV;
+}
+
+static int
+bnx2_test_intr(struct bnx2 *bp)
+{
+       int i;
+       u32 val;
+       u16 status_idx;
+
+       if (!netif_running(bp->dev))
+               return -ENODEV;
+
+       status_idx = REG_RD(bp, BNX2_PCICFG_INT_ACK_CMD) & 0xffff;
+
+       /* This register is not touched during run-time. */
+       val = REG_RD(bp, BNX2_HC_COMMAND);
+       REG_WR(bp, BNX2_HC_COMMAND, val | BNX2_HC_COMMAND_COAL_NOW);
+       REG_RD(bp, BNX2_HC_COMMAND);
+
+       for (i = 0; i < 10; i++) {
+               if ((REG_RD(bp, BNX2_PCICFG_INT_ACK_CMD) & 0xffff) !=
+                       status_idx) {
+
+                       break;
+               }
+
+               msleep_interruptible(10);
+       }
+       if (i < 10)
+               return 0;
+
+       return -ENODEV;
+}
+
+static void
+bnx2_timer(unsigned long data)
+{
+       struct bnx2 *bp = (struct bnx2 *) data;
+       u32 msg;
+
+       if (atomic_read(&bp->intr_sem) != 0)
+               goto bnx2_restart_timer;
+
+       msg = (u32) ++bp->fw_drv_pulse_wr_seq;
+       REG_WR_IND(bp, HOST_VIEW_SHMEM_BASE + BNX2_DRV_PULSE_MB, msg);
+
+       if ((bp->phy_flags & PHY_SERDES_FLAG) &&
+           (CHIP_NUM(bp) == CHIP_NUM_5706)) {
+               unsigned long flags;
+
+               spin_lock_irqsave(&bp->phy_lock, flags);
+               if (bp->serdes_an_pending) {
+                       bp->serdes_an_pending--;
+               }
+               else if ((bp->link_up == 0) && (bp->autoneg & AUTONEG_SPEED)) {
+                       u32 bmcr;
+
+                       bnx2_read_phy(bp, MII_BMCR, &bmcr);
+
+                       if (bmcr & BMCR_ANENABLE) {
+                               u32 phy1, phy2;
+
+                               bnx2_write_phy(bp, 0x1c, 0x7c00);
+                               bnx2_read_phy(bp, 0x1c, &phy1);
+
+                               bnx2_write_phy(bp, 0x17, 0x0f01);
+                               bnx2_read_phy(bp, 0x15, &phy2);
+                               bnx2_write_phy(bp, 0x17, 0x0f01);
+                               bnx2_read_phy(bp, 0x15, &phy2);
+
+                               if ((phy1 & 0x10) &&    /* SIGNAL DETECT */
+                                       !(phy2 & 0x20)) {       /* no CONFIG */
+
+                                       bmcr &= ~BMCR_ANENABLE;
+                                       bmcr |= BMCR_SPEED1000 |
+                                               BMCR_FULLDPLX;
+                                       bnx2_write_phy(bp, MII_BMCR, bmcr);
+                                       bp->phy_flags |=
+                                               PHY_PARALLEL_DETECT_FLAG;
+                               }
+                       }
+               }
+               else if ((bp->link_up) && (bp->autoneg & AUTONEG_SPEED) &&
+                       (bp->phy_flags & PHY_PARALLEL_DETECT_FLAG)) {
+                       u32 phy2;
+
+                       bnx2_write_phy(bp, 0x17, 0x0f01);
+                       bnx2_read_phy(bp, 0x15, &phy2);
+                       if (phy2 & 0x20) {
+                               u32 bmcr;
+
+                               bnx2_read_phy(bp, MII_BMCR, &bmcr);
+                               bmcr |= BMCR_ANENABLE;
+                               bnx2_write_phy(bp, MII_BMCR, bmcr);
+
+                               bp->phy_flags &= ~PHY_PARALLEL_DETECT_FLAG;
+
+                       }
+               }
+
+               spin_unlock_irqrestore(&bp->phy_lock, flags);
+       }
+
+bnx2_restart_timer:
+       bp->timer.expires = RUN_AT(bp->timer_interval);
+
+       add_timer(&bp->timer);
+}
+
+/* Called with rtnl_lock */
+static int
+bnx2_open(struct net_device *dev)
+{
+       struct bnx2 *bp = dev->priv;
+       int rc;
+
+       bnx2_set_power_state(bp, 0);
+       bnx2_disable_int(bp);
+
+       rc = bnx2_alloc_mem(bp);
+       if (rc)
+               return rc;
+
+       if ((CHIP_ID(bp) != CHIP_ID_5706_A0) &&
+               (CHIP_ID(bp) != CHIP_ID_5706_A1) &&
+               !disable_msi) {
+
+               if (pci_enable_msi(bp->pdev) == 0) {
+                       bp->flags |= USING_MSI_FLAG;
+                       rc = request_irq(bp->pdev->irq, bnx2_msi, 0, dev->name,
+                                       dev);
+               }
+               else {
+                       rc = request_irq(bp->pdev->irq, bnx2_interrupt,
+                                       SA_SHIRQ, dev->name, dev);
+               }
+       }
+       else {
+               rc = request_irq(bp->pdev->irq, bnx2_interrupt, SA_SHIRQ,
+                               dev->name, dev);
+       }
+       if (rc) {
+               bnx2_free_mem(bp);
+               return rc;
+       }
+
+       rc = bnx2_init_nic(bp);
+
+       if (rc) {
+               free_irq(bp->pdev->irq, dev);
+               if (bp->flags & USING_MSI_FLAG) {
+                       pci_disable_msi(bp->pdev);
+                       bp->flags &= ~USING_MSI_FLAG;
+               }
+               bnx2_free_skbs(bp);
+               bnx2_free_mem(bp);
+               return rc;
+       }
+       
+       init_timer(&bp->timer);
+
+       bp->timer.expires = RUN_AT(bp->timer_interval);
+       bp->timer.data = (unsigned long) bp;
+       bp->timer.function = bnx2_timer;
+       add_timer(&bp->timer);
+
+       atomic_set(&bp->intr_sem, 0);
+
+       bnx2_enable_int(bp);
+
+       if (bp->flags & USING_MSI_FLAG) {
+               /* Test MSI to make sure it is working
+                * If MSI test fails, go back to INTx mode
+                */
+               if (bnx2_test_intr(bp) != 0) {
+                       printk(KERN_WARNING PFX "%s: No interrupt was generated"
+                              " using MSI, switching to INTx mode. Please"
+                              " report this failure to the PCI maintainer"
+                              " and include system chipset information.\n",
+                              bp->dev->name);
+
+                       bnx2_disable_int(bp);
+                       free_irq(bp->pdev->irq, dev);
+                       pci_disable_msi(bp->pdev);
+                       bp->flags &= ~USING_MSI_FLAG;
+
+                       rc = bnx2_init_nic(bp);
+
+                       if (!rc) {
+                               rc = request_irq(bp->pdev->irq, bnx2_interrupt,
+                                       SA_SHIRQ, dev->name, dev);
+                       }
+                       if (rc) {
+                               bnx2_free_skbs(bp);
+                               bnx2_free_mem(bp);
+                               del_timer_sync(&bp->timer);
+                               return rc;
+                       }
+                       bnx2_enable_int(bp);
+               }
+       }
+       if (bp->flags & USING_MSI_FLAG) {
+               printk(KERN_INFO PFX "%s: using MSI\n", dev->name);
+       }
+
+       netif_start_queue(dev);
+
+       return 0;
+}
+
+static void
+bnx2_reset_task(void *data)
+{
+       struct bnx2 *bp = data;
+
+       bnx2_netif_stop(bp);
+
+       bnx2_init_nic(bp);
+
+       atomic_set(&bp->intr_sem, 1);
+       bnx2_netif_start(bp);
+}
+
+static void
+bnx2_tx_timeout(struct net_device *dev)
+{
+       struct bnx2 *bp = dev->priv;
+
+       /* This allows the netif to be shutdown gracefully before resetting */
+       schedule_work(&bp->reset_task);
+}
+
+#ifdef BCM_VLAN
+/* Called with rtnl_lock */
+static void
+bnx2_vlan_rx_register(struct net_device *dev, struct vlan_group *vlgrp)
+{
+       struct bnx2 *bp = dev->priv;
+
+       bnx2_netif_stop(bp);
+
+       bp->vlgrp = vlgrp;
+       bnx2_set_rx_mode(dev);
+
+       bnx2_netif_start(bp);
+}
+
+/* Called with rtnl_lock */
+static void
+bnx2_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid)
+{
+       struct bnx2 *bp = dev->priv;
+
+       bnx2_netif_stop(bp);
+
+       if (bp->vlgrp)
+               bp->vlgrp->vlan_devices[vid] = NULL;
+       bnx2_set_rx_mode(dev);
+
+       bnx2_netif_start(bp);
+}
+#endif
+
+/* Called with dev->xmit_lock.
+ * hard_start_xmit is pseudo-lockless - a lock is only required when
+ * the tx queue is full. This way, we get the benefit of lockless
+ * operations most of the time without the complexities to handle
+ * netif_stop_queue/wake_queue race conditions.
+ */
+static int
+bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct bnx2 *bp = dev->priv;
+       dma_addr_t mapping;
+       struct tx_bd *txbd;
+       struct sw_bd *tx_buf;
+       u32 len, vlan_tag_flags, last_frag, mss;
+       u16 prod, ring_prod;
+       int i;
+
+       if (unlikely(atomic_read(&bp->tx_avail_bd) <
+               (skb_shinfo(skb)->nr_frags + 1))) {
+
+               netif_stop_queue(dev);
+               printk(KERN_ERR PFX "%s: BUG! Tx ring full when queue awake!\n",
+                       dev->name);
+
+               return NETDEV_TX_BUSY;
+       }
+       len = skb_headlen(skb);
+       prod = bp->tx_prod;
+       ring_prod = TX_RING_IDX(prod);
+
+       vlan_tag_flags = 0;
+       if (skb->ip_summed == CHECKSUM_HW) {
+               vlan_tag_flags |= TX_BD_FLAGS_TCP_UDP_CKSUM;
+       }
+
+       if (bp->vlgrp != 0 && vlan_tx_tag_present(skb)) {
+               vlan_tag_flags |=
+                       (TX_BD_FLAGS_VLAN_TAG | (vlan_tx_tag_get(skb) << 16));
+       }
+#ifdef BCM_TSO 
+       if ((mss = skb_shinfo(skb)->tso_size) &&
+               (skb->len > (bp->dev->mtu + ETH_HLEN))) {
+               u32 tcp_opt_len, ip_tcp_len;
+
+               if (skb_header_cloned(skb) &&
+                   pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
+                       dev_kfree_skb(skb);
+                       return NETDEV_TX_OK;
+               }
+
+               tcp_opt_len = ((skb->h.th->doff - 5) * 4);
+               vlan_tag_flags |= TX_BD_FLAGS_SW_LSO;
+
+               tcp_opt_len = 0;
+               if (skb->h.th->doff > 5) {
+                       tcp_opt_len = (skb->h.th->doff - 5) << 2;
+               }
+               ip_tcp_len = (skb->nh.iph->ihl << 2) + sizeof(struct tcphdr);
+
+               skb->nh.iph->check = 0;
+               skb->nh.iph->tot_len = ntohs(mss + ip_tcp_len + tcp_opt_len);
+               skb->h.th->check =
+                       ~csum_tcpudp_magic(skb->nh.iph->saddr,
+                                           skb->nh.iph->daddr,
+                                           0, IPPROTO_TCP, 0);
+
+               if (tcp_opt_len || (skb->nh.iph->ihl > 5)) {
+                       vlan_tag_flags |= ((skb->nh.iph->ihl - 5) +
+                               (tcp_opt_len >> 2)) << 8;
+               }
+       }
+       else
+#endif
+       {
+               mss = 0;
+       }
+
+       mapping = pci_map_single(bp->pdev, skb->data, len, PCI_DMA_TODEVICE);
+       
+       tx_buf = &bp->tx_buf_ring[ring_prod];
+       tx_buf->skb = skb;
+       pci_unmap_addr_set(tx_buf, mapping, mapping);
+
+       txbd = &bp->tx_desc_ring[ring_prod];
+
+       txbd->tx_bd_haddr_hi = (u64) mapping >> 32;
+       txbd->tx_bd_haddr_lo = (u64) mapping & 0xffffffff;
+       txbd->tx_bd_mss_nbytes = len | (mss << 16);
+       txbd->tx_bd_vlan_tag_flags = vlan_tag_flags | TX_BD_FLAGS_START;
+
+       last_frag = skb_shinfo(skb)->nr_frags;
+
+       for (i = 0; i < last_frag; i++) {
+               skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+               prod = NEXT_TX_BD(prod);
+               ring_prod = TX_RING_IDX(prod);
+               txbd = &bp->tx_desc_ring[ring_prod];
+
+               len = frag->size;
+               mapping = pci_map_page(bp->pdev, frag->page, frag->page_offset,
+                       len, PCI_DMA_TODEVICE);
+               pci_unmap_addr_set(&bp->tx_buf_ring[ring_prod],
+                               mapping, mapping);
+
+               txbd->tx_bd_haddr_hi = (u64) mapping >> 32;
+               txbd->tx_bd_haddr_lo = (u64) mapping & 0xffffffff;
+               txbd->tx_bd_mss_nbytes = len | (mss << 16);
+               txbd->tx_bd_vlan_tag_flags = vlan_tag_flags;
+
+       }
+       txbd->tx_bd_vlan_tag_flags |= TX_BD_FLAGS_END;
+
+       prod = NEXT_TX_BD(prod);
+       bp->tx_prod_bseq += skb->len;
+
+       atomic_sub(last_frag + 1, &bp->tx_avail_bd);
+
+       REG_WR16(bp, MB_TX_CID_ADDR + BNX2_L2CTX_TX_HOST_BIDX, prod);
+       REG_WR(bp, MB_TX_CID_ADDR + BNX2_L2CTX_TX_HOST_BSEQ, bp->tx_prod_bseq);
+
+       mmiowb();
+
+       bp->tx_prod = prod;
+       dev->trans_start = jiffies;
+
+       if (unlikely(atomic_read(&bp->tx_avail_bd) <= MAX_SKB_FRAGS)) {
+               unsigned long flags;
+
+               spin_lock_irqsave(&bp->tx_lock, flags);
+               if (atomic_read(&bp->tx_avail_bd) <= MAX_SKB_FRAGS) {
+                       netif_stop_queue(dev);
+
+                       if (atomic_read(&bp->tx_avail_bd) > MAX_SKB_FRAGS)
+                               netif_wake_queue(dev);
+               }
+               spin_unlock_irqrestore(&bp->tx_lock, flags);
+       }
+
+       return NETDEV_TX_OK;
+}
+
+/* Called with rtnl_lock */
+static int
+bnx2_close(struct net_device *dev)
+{
+       struct bnx2 *bp = dev->priv;
+       u32 reset_code;
+
+       flush_scheduled_work();
+       bnx2_netif_stop(bp);
+       del_timer_sync(&bp->timer);
+       if (bp->wol)
+               reset_code = BNX2_DRV_MSG_CODE_SUSPEND_WOL;
+       else
+               reset_code = BNX2_DRV_MSG_CODE_SUSPEND_NO_WOL;
+       bnx2_reset_chip(bp, reset_code);
+       free_irq(bp->pdev->irq, dev);
+       if (bp->flags & USING_MSI_FLAG) {
+               pci_disable_msi(bp->pdev);
+               bp->flags &= ~USING_MSI_FLAG;
+       }
+       bnx2_free_skbs(bp);
+       bnx2_free_mem(bp);
+       bp->link_up = 0;
+       netif_carrier_off(bp->dev);
+       bnx2_set_power_state(bp, 3);
+       return 0;
+}
+
+#define GET_NET_STATS64(ctr)                                   \
+       (unsigned long) ((unsigned long) (ctr##_hi) << 32) +    \
+       (unsigned long) (ctr##_lo)
+
+#define GET_NET_STATS32(ctr)           \
+       (ctr##_lo)
+
+#if (BITS_PER_LONG == 64)
+#define GET_NET_STATS  GET_NET_STATS64
+#else
+#define GET_NET_STATS  GET_NET_STATS32
+#endif
+
+static struct net_device_stats *
+bnx2_get_stats(struct net_device *dev)
+{
+       struct bnx2 *bp = dev->priv;
+       struct statistics_block *stats_blk = bp->stats_blk;
+       struct net_device_stats *net_stats = &bp->net_stats;
+
+       if (bp->stats_blk == NULL) {
+               return net_stats;
+       }
+       net_stats->rx_packets =
+               GET_NET_STATS(stats_blk->stat_IfHCInUcastPkts) +
+               GET_NET_STATS(stats_blk->stat_IfHCInMulticastPkts) +
+               GET_NET_STATS(stats_blk->stat_IfHCInBroadcastPkts);
+
+       net_stats->tx_packets =
+               GET_NET_STATS(stats_blk->stat_IfHCOutUcastPkts) +
+               GET_NET_STATS(stats_blk->stat_IfHCOutMulticastPkts) +
+               GET_NET_STATS(stats_blk->stat_IfHCOutBroadcastPkts);
+
+       net_stats->rx_bytes =
+               GET_NET_STATS(stats_blk->stat_IfHCInOctets);
+
+       net_stats->tx_bytes =
+               GET_NET_STATS(stats_blk->stat_IfHCOutOctets);
+
+       net_stats->multicast = 
+               GET_NET_STATS(stats_blk->stat_IfHCOutMulticastPkts);
+
+       net_stats->collisions = 
+               (unsigned long) stats_blk->stat_EtherStatsCollisions;
+
+       net_stats->rx_length_errors = 
+               (unsigned long) (stats_blk->stat_EtherStatsUndersizePkts +
+               stats_blk->stat_EtherStatsOverrsizePkts);
+
+       net_stats->rx_over_errors = 
+               (unsigned long) stats_blk->stat_IfInMBUFDiscards;
+
+       net_stats->rx_frame_errors = 
+               (unsigned long) stats_blk->stat_Dot3StatsAlignmentErrors;
+
+       net_stats->rx_crc_errors = 
+               (unsigned long) stats_blk->stat_Dot3StatsFCSErrors;
+
+       net_stats->rx_errors = net_stats->rx_length_errors +
+               net_stats->rx_over_errors + net_stats->rx_frame_errors +
+               net_stats->rx_crc_errors;
+
+       net_stats->tx_aborted_errors =
+               (unsigned long) (stats_blk->stat_Dot3StatsExcessiveCollisions +
+               stats_blk->stat_Dot3StatsLateCollisions);
+
+       if (CHIP_NUM(bp) == CHIP_NUM_5706)
+               net_stats->tx_carrier_errors = 0;
+       else {
+               net_stats->tx_carrier_errors =
+                       (unsigned long)
+                       stats_blk->stat_Dot3StatsCarrierSenseErrors;
+       }
+
+       net_stats->tx_errors =
+               (unsigned long) 
+               stats_blk->stat_emac_tx_stat_dot3statsinternalmactransmiterrors
+               +
+               net_stats->tx_aborted_errors +
+               net_stats->tx_carrier_errors;
+
+       return net_stats;
+}
+
+/* All ethtool functions called with rtnl_lock */
+
+static int
+bnx2_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct bnx2 *bp = dev->priv;
+
+       cmd->supported = SUPPORTED_Autoneg;
+       if (bp->phy_flags & PHY_SERDES_FLAG) {
+               cmd->supported |= SUPPORTED_1000baseT_Full |
+                       SUPPORTED_FIBRE;
+
+               cmd->port = PORT_FIBRE;
+       }
+       else {
+               cmd->supported |= SUPPORTED_10baseT_Half |
+                       SUPPORTED_10baseT_Full |
+                       SUPPORTED_100baseT_Half |
+                       SUPPORTED_100baseT_Full |
+                       SUPPORTED_1000baseT_Full |
+                       SUPPORTED_TP;
+
+               cmd->port = PORT_TP;
+       }
+
+       cmd->advertising = bp->advertising;
+
+       if (bp->autoneg & AUTONEG_SPEED) {
+               cmd->autoneg = AUTONEG_ENABLE;
+       }
+       else {
+               cmd->autoneg = AUTONEG_DISABLE;
+       }
+
+       if (netif_carrier_ok(dev)) {
+               cmd->speed = bp->line_speed;
+               cmd->duplex = bp->duplex;
+       }
+       else {
+               cmd->speed = -1;
+               cmd->duplex = -1;
+       }
+
+       cmd->transceiver = XCVR_INTERNAL;
+       cmd->phy_address = bp->phy_addr;
+
+       return 0;
+}
+  
+static int
+bnx2_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct bnx2 *bp = dev->priv;
+       u8 autoneg = bp->autoneg;
+       u8 req_duplex = bp->req_duplex;
+       u16 req_line_speed = bp->req_line_speed;
+       u32 advertising = bp->advertising;
+
+       if (cmd->autoneg == AUTONEG_ENABLE) {
+               autoneg |= AUTONEG_SPEED;
+
+               cmd->advertising &= ETHTOOL_ALL_COPPER_SPEED; 
+
+               /* allow advertising 1 speed */
+               if ((cmd->advertising == ADVERTISED_10baseT_Half) ||
+                       (cmd->advertising == ADVERTISED_10baseT_Full) ||
+                       (cmd->advertising == ADVERTISED_100baseT_Half) ||
+                       (cmd->advertising == ADVERTISED_100baseT_Full)) {
+
+                       if (bp->phy_flags & PHY_SERDES_FLAG)
+                               return -EINVAL;
+
+                       advertising = cmd->advertising;
+
+               }
+               else if (cmd->advertising == ADVERTISED_1000baseT_Full) {
+                       advertising = cmd->advertising;
+               }
+               else if (cmd->advertising == ADVERTISED_1000baseT_Half) {
+                       return -EINVAL;
+               }
+               else {
+                       if (bp->phy_flags & PHY_SERDES_FLAG) {
+                               advertising = ETHTOOL_ALL_FIBRE_SPEED;
+                       }
+                       else {
+                               advertising = ETHTOOL_ALL_COPPER_SPEED;
+                       }
+               }
+               advertising |= ADVERTISED_Autoneg;
+       }
+       else {
+               if (bp->phy_flags & PHY_SERDES_FLAG) {
+                       if ((cmd->speed != SPEED_1000) ||
+                               (cmd->duplex != DUPLEX_FULL)) {
+                               return -EINVAL;
+                       }
+               }
+               else if (cmd->speed == SPEED_1000) {
+                       return -EINVAL;
+               }
+               autoneg &= ~AUTONEG_SPEED;
+               req_line_speed = cmd->speed;
+               req_duplex = cmd->duplex;
+               advertising = 0;
+       }
+
+       bp->autoneg = autoneg;
+       bp->advertising = advertising;
+       bp->req_line_speed = req_line_speed;
+       bp->req_duplex = req_duplex;
+
+       spin_lock_irq(&bp->phy_lock);
+
+       bnx2_setup_phy(bp);
+
+       spin_unlock_irq(&bp->phy_lock);
+
+       return 0;
+}
+
+static void
+bnx2_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+       struct bnx2 *bp = dev->priv;
+
+       strcpy(info->driver, DRV_MODULE_NAME);
+       strcpy(info->version, DRV_MODULE_VERSION);
+       strcpy(info->bus_info, pci_name(bp->pdev));
+       info->fw_version[0] = ((bp->fw_ver & 0xff000000) >> 24) + '0';
+       info->fw_version[2] = ((bp->fw_ver & 0xff0000) >> 16) + '0';
+       info->fw_version[4] = ((bp->fw_ver & 0xff00) >> 8) + '0';
+       info->fw_version[6] = (bp->fw_ver & 0xff) + '0';
+       info->fw_version[1] = info->fw_version[3] = info->fw_version[5] = '.';
+       info->fw_version[7] = 0;
+}
+
+static void
+bnx2_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+       struct bnx2 *bp = dev->priv;
+
+       if (bp->flags & NO_WOL_FLAG) {
+               wol->supported = 0;
+               wol->wolopts = 0;
+       }
+       else {
+               wol->supported = WAKE_MAGIC;
+               if (bp->wol)
+                       wol->wolopts = WAKE_MAGIC;
+               else
+                       wol->wolopts = 0;
+       }
+       memset(&wol->sopass, 0, sizeof(wol->sopass));
+}
+
+static int
+bnx2_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+       struct bnx2 *bp = dev->priv;
+
+       if (wol->wolopts & ~WAKE_MAGIC)
+               return -EINVAL;
+
+       if (wol->wolopts & WAKE_MAGIC) {
+               if (bp->flags & NO_WOL_FLAG)
+                       return -EINVAL;
+
+               bp->wol = 1;
+       }
+       else {
+               bp->wol = 0;
+       }
+       return 0;
+}
+
+static int
+bnx2_nway_reset(struct net_device *dev)
+{
+       struct bnx2 *bp = dev->priv;
+       u32 bmcr;
+
+       if (!(bp->autoneg & AUTONEG_SPEED)) {
+               return -EINVAL;
+       }
+
+       spin_lock_irq(&bp->phy_lock);
+
+       /* Force a link down visible on the other side */
+       if (bp->phy_flags & PHY_SERDES_FLAG) {
+               bnx2_write_phy(bp, MII_BMCR, BMCR_LOOPBACK);
+               spin_unlock_irq(&bp->phy_lock);
+
+               msleep(20);
+
+               spin_lock_irq(&bp->phy_lock);
+               if (CHIP_NUM(bp) == CHIP_NUM_5706) {
+                       bp->serdes_an_pending = SERDES_AN_TIMEOUT /
+                               bp->timer_interval;
+               }
+       }
+
+       bnx2_read_phy(bp, MII_BMCR, &bmcr);
+       bmcr &= ~BMCR_LOOPBACK;
+       bnx2_write_phy(bp, MII_BMCR, bmcr | BMCR_ANRESTART | BMCR_ANENABLE);
+
+       spin_unlock_irq(&bp->phy_lock);
+
+       return 0;
+}
+
+static int
+bnx2_get_eeprom_len(struct net_device *dev)
+{
+       struct bnx2 *bp = dev->priv;
+
+       if (bp->flash_info == 0)
+               return 0;
+
+       return (int) bp->flash_info->total_size;
+}
+
+static int
+bnx2_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
+               u8 *eebuf)
+{
+       struct bnx2 *bp = dev->priv;
+       int rc;
+
+       if (eeprom->offset > bp->flash_info->total_size)
+               return -EINVAL;
+
+       if ((eeprom->offset + eeprom->len) > bp->flash_info->total_size)
+               eeprom->len = bp->flash_info->total_size - eeprom->offset;
+
+       rc = bnx2_nvram_read(bp, eeprom->offset, eebuf, eeprom->len);
+
+       return rc;
+}
+
+static int
+bnx2_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
+               u8 *eebuf)
+{
+       struct bnx2 *bp = dev->priv;
+       int rc;
+
+       if (eeprom->offset > bp->flash_info->total_size)
+               return -EINVAL;
+
+       if ((eeprom->offset + eeprom->len) > bp->flash_info->total_size)
+               eeprom->len = bp->flash_info->total_size - eeprom->offset;
+
+       rc = bnx2_nvram_write(bp, eeprom->offset, eebuf, eeprom->len);
+
+       return rc;
+}
+
+static int
+bnx2_get_coalesce(struct net_device *dev, struct ethtool_coalesce *coal)
+{
+       struct bnx2 *bp = dev->priv;
+
+       memset(coal, 0, sizeof(struct ethtool_coalesce));
+
+       coal->rx_coalesce_usecs = bp->rx_ticks;
+       coal->rx_max_coalesced_frames = bp->rx_quick_cons_trip;
+       coal->rx_coalesce_usecs_irq = bp->rx_ticks_int;
+       coal->rx_max_coalesced_frames_irq = bp->rx_quick_cons_trip_int;
+
+       coal->tx_coalesce_usecs = bp->tx_ticks;
+       coal->tx_max_coalesced_frames = bp->tx_quick_cons_trip;
+       coal->tx_coalesce_usecs_irq = bp->tx_ticks_int;
+       coal->tx_max_coalesced_frames_irq = bp->tx_quick_cons_trip_int;
+
+       coal->stats_block_coalesce_usecs = bp->stats_ticks;
+
+       return 0;
+}
+
+static int
+bnx2_set_coalesce(struct net_device *dev, struct ethtool_coalesce *coal)
+{
+       struct bnx2 *bp = dev->priv;
+
+       bp->rx_ticks = (u16) coal->rx_coalesce_usecs;
+       if (bp->rx_ticks > 0x3ff) bp->rx_ticks = 0x3ff;
+
+       bp->rx_quick_cons_trip = (u16) coal->rx_max_coalesced_frames; 
+       if (bp->rx_quick_cons_trip > 0xff) bp->rx_quick_cons_trip = 0xff;
+
+       bp->rx_ticks_int = (u16) coal->rx_coalesce_usecs_irq;
+       if (bp->rx_ticks_int > 0x3ff) bp->rx_ticks_int = 0x3ff;
+
+       bp->rx_quick_cons_trip_int = (u16) coal->rx_max_coalesced_frames_irq;
+       if (bp->rx_quick_cons_trip_int > 0xff)
+               bp->rx_quick_cons_trip_int = 0xff;
+
+       bp->tx_ticks = (u16) coal->tx_coalesce_usecs;
+       if (bp->tx_ticks > 0x3ff) bp->tx_ticks = 0x3ff;
+
+       bp->tx_quick_cons_trip = (u16) coal->tx_max_coalesced_frames;
+       if (bp->tx_quick_cons_trip > 0xff) bp->tx_quick_cons_trip = 0xff;
+
+       bp->tx_ticks_int = (u16) coal->tx_coalesce_usecs_irq;
+       if (bp->tx_ticks_int > 0x3ff) bp->tx_ticks_int = 0x3ff;
+
+       bp->tx_quick_cons_trip_int = (u16) coal->tx_max_coalesced_frames_irq;
+       if (bp->tx_quick_cons_trip_int > 0xff) bp->tx_quick_cons_trip_int =
+               0xff;
+
+       bp->stats_ticks = coal->stats_block_coalesce_usecs;
+       if (bp->stats_ticks > 0xffff00) bp->stats_ticks = 0xffff00;
+       bp->stats_ticks &= 0xffff00;
+
+       if (netif_running(bp->dev)) {
+               bnx2_netif_stop(bp);
+               bnx2_init_nic(bp);
+               bnx2_netif_start(bp);
+       }
+
+       return 0;
+}
+
+static void
+bnx2_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ering)
+{
+       struct bnx2 *bp = dev->priv;
+
+       ering->rx_max_pending = MAX_RX_DESC_CNT;
+       ering->rx_mini_max_pending = 0;
+       ering->rx_jumbo_max_pending = 0;
+
+       ering->rx_pending = bp->rx_ring_size;
+       ering->rx_mini_pending = 0;
+       ering->rx_jumbo_pending = 0;
+
+       ering->tx_max_pending = MAX_TX_DESC_CNT;
+       ering->tx_pending = bp->tx_ring_size;
+}
+
+static int
+bnx2_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ering)
+{
+       struct bnx2 *bp = dev->priv;
+
+       if ((ering->rx_pending > MAX_RX_DESC_CNT) ||
+               (ering->tx_pending > MAX_TX_DESC_CNT) ||
+               (ering->tx_pending <= MAX_SKB_FRAGS)) {
+
+               return -EINVAL;
+       }
+       bp->rx_ring_size = ering->rx_pending;
+       bp->tx_ring_size = ering->tx_pending;
+
+       if (netif_running(bp->dev)) {
+               bnx2_netif_stop(bp);
+               bnx2_init_nic(bp);
+               bnx2_netif_start(bp);
+       }
+
+       return 0;
+}
+
+static void
+bnx2_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *epause)
+{
+       struct bnx2 *bp = dev->priv;
+
+       epause->autoneg = ((bp->autoneg & AUTONEG_FLOW_CTRL) != 0);
+       epause->rx_pause = ((bp->flow_ctrl & FLOW_CTRL_RX) != 0);
+       epause->tx_pause = ((bp->flow_ctrl & FLOW_CTRL_TX) != 0);
+}
+
+static int
+bnx2_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam *epause)
+{
+       struct bnx2 *bp = dev->priv;
+
+       bp->req_flow_ctrl = 0;
+       if (epause->rx_pause)
+               bp->req_flow_ctrl |= FLOW_CTRL_RX;
+       if (epause->tx_pause)
+               bp->req_flow_ctrl |= FLOW_CTRL_TX;
+
+       if (epause->autoneg) {
+               bp->autoneg |= AUTONEG_FLOW_CTRL;
+       }
+       else {
+               bp->autoneg &= ~AUTONEG_FLOW_CTRL;
+       }
+
+       spin_lock_irq(&bp->phy_lock);
+
+       bnx2_setup_phy(bp);
+
+       spin_unlock_irq(&bp->phy_lock);
+
+       return 0;
+}
+
+static u32
+bnx2_get_rx_csum(struct net_device *dev)
+{
+       struct bnx2 *bp = dev->priv;
+
+       return bp->rx_csum;
+}
+
+static int
+bnx2_set_rx_csum(struct net_device *dev, u32 data)
+{
+       struct bnx2 *bp = dev->priv;
+
+       bp->rx_csum = data;
+       return 0;
+}
+
+#define BNX2_NUM_STATS 45
+
+struct {
+       char string[ETH_GSTRING_LEN];
+} bnx2_stats_str_arr[BNX2_NUM_STATS] = {
+       { "rx_bytes" },
+       { "rx_error_bytes" },
+       { "tx_bytes" },
+       { "tx_error_bytes" },
+       { "rx_ucast_packets" },
+       { "rx_mcast_packets" },
+       { "rx_bcast_packets" },
+       { "tx_ucast_packets" },
+       { "tx_mcast_packets" },
+       { "tx_bcast_packets" },
+       { "tx_mac_errors" },
+       { "tx_carrier_errors" },
+       { "rx_crc_errors" },
+       { "rx_align_errors" },
+       { "tx_single_collisions" },
+       { "tx_multi_collisions" },
+       { "tx_deferred" },
+       { "tx_excess_collisions" },
+       { "tx_late_collisions" },
+       { "tx_total_collisions" },
+       { "rx_fragments" },
+       { "rx_jabbers" },
+       { "rx_undersize_packets" },
+       { "rx_oversize_packets" },
+       { "rx_64_byte_packets" },
+       { "rx_65_to_127_byte_packets" },
+       { "rx_128_to_255_byte_packets" },
+       { "rx_256_to_511_byte_packets" },
+       { "rx_512_to_1023_byte_packets" },
+       { "rx_1024_to_1522_byte_packets" },
+       { "rx_1523_to_9022_byte_packets" },
+       { "tx_64_byte_packets" },
+       { "tx_65_to_127_byte_packets" },
+       { "tx_128_to_255_byte_packets" },
+       { "tx_256_to_511_byte_packets" },
+       { "tx_512_to_1023_byte_packets" },
+       { "tx_1024_to_1522_byte_packets" },
+       { "tx_1523_to_9022_byte_packets" },
+       { "rx_xon_frames" },
+       { "rx_xoff_frames" },
+       { "tx_xon_frames" },
+       { "tx_xoff_frames" },
+       { "rx_mac_ctrl_frames" },
+       { "rx_filtered_packets" },
+       { "rx_discards" },
+};
+
+#define STATS_OFFSET32(offset_name) (offsetof(struct statistics_block, offset_name) / 4)
+
+unsigned long bnx2_stats_offset_arr[BNX2_NUM_STATS] = {
+    STATS_OFFSET32(stat_IfHCInOctets_hi),
+    STATS_OFFSET32(stat_IfHCInBadOctets_hi),
+    STATS_OFFSET32(stat_IfHCOutOctets_hi),
+    STATS_OFFSET32(stat_IfHCOutBadOctets_hi),
+    STATS_OFFSET32(stat_IfHCInUcastPkts_hi),
+    STATS_OFFSET32(stat_IfHCInMulticastPkts_hi),
+    STATS_OFFSET32(stat_IfHCInBroadcastPkts_hi),
+    STATS_OFFSET32(stat_IfHCOutUcastPkts_hi),
+    STATS_OFFSET32(stat_IfHCOutMulticastPkts_hi),
+    STATS_OFFSET32(stat_IfHCOutBroadcastPkts_hi),
+    STATS_OFFSET32(stat_emac_tx_stat_dot3statsinternalmactransmiterrors),
+    STATS_OFFSET32(stat_Dot3StatsCarrierSenseErrors),                 
+    STATS_OFFSET32(stat_Dot3StatsFCSErrors),                          
+    STATS_OFFSET32(stat_Dot3StatsAlignmentErrors),                    
+    STATS_OFFSET32(stat_Dot3StatsSingleCollisionFrames),              
+    STATS_OFFSET32(stat_Dot3StatsMultipleCollisionFrames),            
+    STATS_OFFSET32(stat_Dot3StatsDeferredTransmissions),              
+    STATS_OFFSET32(stat_Dot3StatsExcessiveCollisions),                
+    STATS_OFFSET32(stat_Dot3StatsLateCollisions),                     
+    STATS_OFFSET32(stat_EtherStatsCollisions),                        
+    STATS_OFFSET32(stat_EtherStatsFragments),                         
+    STATS_OFFSET32(stat_EtherStatsJabbers),                           
+    STATS_OFFSET32(stat_EtherStatsUndersizePkts),                     
+    STATS_OFFSET32(stat_EtherStatsOverrsizePkts),                     
+    STATS_OFFSET32(stat_EtherStatsPktsRx64Octets),                    
+    STATS_OFFSET32(stat_EtherStatsPktsRx65Octetsto127Octets),         
+    STATS_OFFSET32(stat_EtherStatsPktsRx128Octetsto255Octets),        
+    STATS_OFFSET32(stat_EtherStatsPktsRx256Octetsto511Octets),        
+    STATS_OFFSET32(stat_EtherStatsPktsRx512Octetsto1023Octets),       
+    STATS_OFFSET32(stat_EtherStatsPktsRx1024Octetsto1522Octets),      
+    STATS_OFFSET32(stat_EtherStatsPktsRx1523Octetsto9022Octets),      
+    STATS_OFFSET32(stat_EtherStatsPktsTx64Octets),                    
+    STATS_OFFSET32(stat_EtherStatsPktsTx65Octetsto127Octets),         
+    STATS_OFFSET32(stat_EtherStatsPktsTx128Octetsto255Octets),        
+    STATS_OFFSET32(stat_EtherStatsPktsTx256Octetsto511Octets),        
+    STATS_OFFSET32(stat_EtherStatsPktsTx512Octetsto1023Octets),       
+    STATS_OFFSET32(stat_EtherStatsPktsTx1024Octetsto1522Octets),      
+    STATS_OFFSET32(stat_EtherStatsPktsTx1523Octetsto9022Octets),      
+    STATS_OFFSET32(stat_XonPauseFramesReceived),                      
+    STATS_OFFSET32(stat_XoffPauseFramesReceived),                     
+    STATS_OFFSET32(stat_OutXonSent),                                  
+    STATS_OFFSET32(stat_OutXoffSent),                                 
+    STATS_OFFSET32(stat_MacControlFramesReceived),                    
+    STATS_OFFSET32(stat_IfInFramesL2FilterDiscards),                  
+    STATS_OFFSET32(stat_IfInMBUFDiscards),                            
+};
+
+/* stat_IfHCInBadOctets and stat_Dot3StatsCarrierSenseErrors are
+ * skipped because of errata.
+ */               
+u8 bnx2_5706_stats_len_arr[BNX2_NUM_STATS] = {
+       8,0,8,8,8,8,8,8,8,8,
+       4,0,4,4,4,4,4,4,4,4,
+       4,4,4,4,4,4,4,4,4,4,
+       4,4,4,4,4,4,4,4,4,4,
+       4,4,4,4,4,
+};
+
+#define BNX2_NUM_TESTS 6
+
+struct {
+       char string[ETH_GSTRING_LEN];
+} bnx2_tests_str_arr[BNX2_NUM_TESTS] = {
+       { "register_test (offline)" },
+       { "memory_test (offline)" },
+       { "loopback_test (offline)" },
+       { "nvram_test (online)" },
+       { "interrupt_test (online)" },
+       { "link_test (online)" },
+};
+
+static int
+bnx2_self_test_count(struct net_device *dev)
+{
+       return BNX2_NUM_TESTS;
+}
+
+static void
+bnx2_self_test(struct net_device *dev, struct ethtool_test *etest, u64 *buf)
+{
+       struct bnx2 *bp = dev->priv;
+
+       memset(buf, 0, sizeof(u64) * BNX2_NUM_TESTS);
+       if (etest->flags & ETH_TEST_FL_OFFLINE) {
+               bnx2_netif_stop(bp);
+               bnx2_reset_chip(bp, BNX2_DRV_MSG_CODE_DIAG);
+               bnx2_free_skbs(bp);
+
+               if (bnx2_test_registers(bp) != 0) {
+                       buf[0] = 1;
+                       etest->flags |= ETH_TEST_FL_FAILED;
+               }
+               if (bnx2_test_memory(bp) != 0) {
+                       buf[1] = 1;
+                       etest->flags |= ETH_TEST_FL_FAILED;
+               }
+               if (bnx2_test_loopback(bp) != 0) {
+                       buf[2] = 1;
+                       etest->flags |= ETH_TEST_FL_FAILED;
+               }
+
+               if (!netif_running(bp->dev)) {
+                       bnx2_reset_chip(bp, BNX2_DRV_MSG_CODE_RESET);
+               }
+               else {
+                       bnx2_init_nic(bp);
+                       bnx2_netif_start(bp);
+               }
+
+               /* wait for link up */
+               msleep_interruptible(3000);
+               if ((!bp->link_up) && !(bp->phy_flags & PHY_SERDES_FLAG))
+                       msleep_interruptible(4000);
+       }
+
+       if (bnx2_test_nvram(bp) != 0) {
+               buf[3] = 1;
+               etest->flags |= ETH_TEST_FL_FAILED;
+       }
+       if (bnx2_test_intr(bp) != 0) {
+               buf[4] = 1;
+               etest->flags |= ETH_TEST_FL_FAILED;
+       }
+
+       if (bnx2_test_link(bp) != 0) {
+               buf[5] = 1;
+               etest->flags |= ETH_TEST_FL_FAILED;
+
+       }
+}
+
+static void
+bnx2_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
+{
+       switch (stringset) {
+       case ETH_SS_STATS:
+               memcpy(buf, bnx2_stats_str_arr,
+                       sizeof(bnx2_stats_str_arr));
+               break;
+       case ETH_SS_TEST:
+               memcpy(buf, bnx2_tests_str_arr,
+                       sizeof(bnx2_tests_str_arr));
+               break;
+       }
+}
+
+static int
+bnx2_get_stats_count(struct net_device *dev)
+{
+       return BNX2_NUM_STATS;
+}
+
+static void
+bnx2_get_ethtool_stats(struct net_device *dev,
+               struct ethtool_stats *stats, u64 *buf)
+{
+       struct bnx2 *bp = dev->priv;
+       int i;
+       u32 *hw_stats = (u32 *) bp->stats_blk;
+       u8 *stats_len_arr = 0;
+
+       if (hw_stats == NULL) {
+               memset(buf, 0, sizeof(u64) * BNX2_NUM_STATS);
+               return;
+       }
+
+       if (CHIP_NUM(bp) == CHIP_NUM_5706)
+               stats_len_arr = bnx2_5706_stats_len_arr;
+
+       for (i = 0; i < BNX2_NUM_STATS; i++) {
+               if (stats_len_arr[i] == 0) {
+                       /* skip this counter */
+                       buf[i] = 0;
+                       continue;
+               }
+               if (stats_len_arr[i] == 4) {
+                       /* 4-byte counter */
+                       buf[i] = (u64)
+                               *(hw_stats + bnx2_stats_offset_arr[i]);
+                       continue;
+               }
+               /* 8-byte counter */
+               buf[i] = (((u64) *(hw_stats +
+                                       bnx2_stats_offset_arr[i])) << 32) +
+                               *(hw_stats + bnx2_stats_offset_arr[i] + 1);
+       }
+}
+
+static int
+bnx2_phys_id(struct net_device *dev, u32 data)
+{
+       struct bnx2 *bp = dev->priv;
+       int i;
+       u32 save;
+
+       if (data == 0)
+               data = 2;
+
+       save = REG_RD(bp, BNX2_MISC_CFG);
+       REG_WR(bp, BNX2_MISC_CFG, BNX2_MISC_CFG_LEDMODE_MAC);
+
+       for (i = 0; i < (data * 2); i++) {
+               if ((i % 2) == 0) {
+                       REG_WR(bp, BNX2_EMAC_LED, BNX2_EMAC_LED_OVERRIDE);
+               }
+               else {
+                       REG_WR(bp, BNX2_EMAC_LED, BNX2_EMAC_LED_OVERRIDE |
+                               BNX2_EMAC_LED_1000MB_OVERRIDE |
+                               BNX2_EMAC_LED_100MB_OVERRIDE |
+                               BNX2_EMAC_LED_10MB_OVERRIDE |
+                               BNX2_EMAC_LED_TRAFFIC_OVERRIDE |
+                               BNX2_EMAC_LED_TRAFFIC);
+               }
+               msleep_interruptible(500);
+               if (signal_pending(current))
+                       break;
+       }
+       REG_WR(bp, BNX2_EMAC_LED, 0);
+       REG_WR(bp, BNX2_MISC_CFG, save);
+       return 0;
+}
+
+static struct ethtool_ops bnx2_ethtool_ops = {
+       .get_settings           = bnx2_get_settings,
+       .set_settings           = bnx2_set_settings,
+       .get_drvinfo            = bnx2_get_drvinfo,
+       .get_wol                = bnx2_get_wol,
+       .set_wol                = bnx2_set_wol,
+       .nway_reset             = bnx2_nway_reset,
+       .get_link               = ethtool_op_get_link,
+       .get_eeprom_len         = bnx2_get_eeprom_len,
+       .get_eeprom             = bnx2_get_eeprom,
+       .set_eeprom             = bnx2_set_eeprom,
+       .get_coalesce           = bnx2_get_coalesce,
+       .set_coalesce           = bnx2_set_coalesce,
+       .get_ringparam          = bnx2_get_ringparam,
+       .set_ringparam          = bnx2_set_ringparam,
+       .get_pauseparam         = bnx2_get_pauseparam,
+       .set_pauseparam         = bnx2_set_pauseparam,
+       .get_rx_csum            = bnx2_get_rx_csum,
+       .set_rx_csum            = bnx2_set_rx_csum,
+       .get_tx_csum            = ethtool_op_get_tx_csum,
+       .set_tx_csum            = ethtool_op_set_tx_csum,
+       .get_sg                 = ethtool_op_get_sg,
+       .set_sg                 = ethtool_op_set_sg,
+#ifdef BCM_TSO
+       .get_tso                = ethtool_op_get_tso,
+       .set_tso                = ethtool_op_set_tso,
+#endif
+       .self_test_count        = bnx2_self_test_count,
+       .self_test              = bnx2_self_test,
+       .get_strings            = bnx2_get_strings,
+       .phys_id                = bnx2_phys_id,
+       .get_stats_count        = bnx2_get_stats_count,
+       .get_ethtool_stats      = bnx2_get_ethtool_stats,
+};
+
+/* Called with rtnl_lock */
+static int
+bnx2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data;
+       struct bnx2 *bp = dev->priv;
+       int err;
+
+       switch(cmd) {
+       case SIOCGMIIPHY:
+               data->phy_id = bp->phy_addr;
+
+               /* fallthru */
+       case SIOCGMIIREG: {
+               u32 mii_regval;
+
+               spin_lock_irq(&bp->phy_lock);
+               err = bnx2_read_phy(bp, data->reg_num & 0x1f, &mii_regval);
+               spin_unlock_irq(&bp->phy_lock);
+
+               data->val_out = mii_regval;
+
+               return err;
+       }
+
+       case SIOCSMIIREG:
+               if (!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+
+               spin_lock_irq(&bp->phy_lock);
+               err = bnx2_write_phy(bp, data->reg_num & 0x1f, data->val_in);
+               spin_unlock_irq(&bp->phy_lock);
+
+               return err;
+
+       default:
+               /* do nothing */
+               break;
+       }
+       return -EOPNOTSUPP;
+}
+
+/* Called with rtnl_lock */
+static int
+bnx2_change_mac_addr(struct net_device *dev, void *p)
+{
+       struct sockaddr *addr = p;
+       struct bnx2 *bp = dev->priv;
+
+       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+       if (netif_running(dev))
+               bnx2_set_mac_addr(bp);
+
+       return 0;
+}
+
+/* Called with rtnl_lock */
+static int
+bnx2_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct bnx2 *bp = dev->priv;
+
+       if (((new_mtu + ETH_HLEN) > MAX_ETHERNET_JUMBO_PACKET_SIZE) ||
+               ((new_mtu + ETH_HLEN) < MIN_ETHERNET_PACKET_SIZE))
+               return -EINVAL;
+
+       dev->mtu = new_mtu;
+       if (netif_running(dev)) {
+               bnx2_netif_stop(bp);
+
+               bnx2_init_nic(bp);
+
+               bnx2_netif_start(bp);
+       }
+       return 0;
+}
+
+#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER)
+static void
+poll_bnx2(struct net_device *dev)
+{
+       struct bnx2 *bp = dev->priv;
+
+       disable_irq(bp->pdev->irq);
+       bnx2_interrupt(bp->pdev->irq, dev, NULL);
+       enable_irq(bp->pdev->irq);
+}
+#endif
+
+static int __devinit
+bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
+{
+       struct bnx2 *bp;
+       unsigned long mem_len;
+       int rc;
+       u32 reg;
+
+       SET_MODULE_OWNER(dev);
+       SET_NETDEV_DEV(dev, &pdev->dev);
+       bp = dev->priv;
+
+       bp->flags = 0;
+       bp->phy_flags = 0;
+
+       /* enable device (incl. PCI PM wakeup), and bus-mastering */
+       rc = pci_enable_device(pdev);
+       if (rc) {
+               printk(KERN_ERR PFX "Cannot enable PCI device, aborting.");
+               goto err_out;
+       }
+
+       if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+               printk(KERN_ERR PFX "Cannot find PCI device base address, "
+                      "aborting.\n");
+               rc = -ENODEV;
+               goto err_out_disable;
+       }
+
+       rc = pci_request_regions(pdev, DRV_MODULE_NAME);
+       if (rc) {
+               printk(KERN_ERR PFX "Cannot obtain PCI resources, aborting.\n");
+               goto err_out_disable;
+       }
+
+       pci_set_master(pdev);
+
+       bp->pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
+       if (bp->pm_cap == 0) {
+               printk(KERN_ERR PFX "Cannot find power management capability, "
+                              "aborting.\n");
+               rc = -EIO;
+               goto err_out_release;
+       }
+
+       bp->pcix_cap = pci_find_capability(pdev, PCI_CAP_ID_PCIX);
+       if (bp->pcix_cap == 0) {
+               printk(KERN_ERR PFX "Cannot find PCIX capability, aborting.\n");
+               rc = -EIO;
+               goto err_out_release;
+       }
+
+       if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0) {
+               bp->flags |= USING_DAC_FLAG;
+               if (pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK) != 0) {
+                       printk(KERN_ERR PFX "pci_set_consistent_dma_mask "
+                              "failed, aborting.\n");
+                       rc = -EIO;
+                       goto err_out_release;
+               }
+       }
+       else if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) {
+               printk(KERN_ERR PFX "System does not support DMA, aborting.\n");
+               rc = -EIO;
+               goto err_out_release;
+       }
+
+       bp->dev = dev;
+       bp->pdev = pdev;
+
+       spin_lock_init(&bp->phy_lock);
+       spin_lock_init(&bp->tx_lock);
+       INIT_WORK(&bp->reset_task, bnx2_reset_task, bp);
+
+       dev->base_addr = dev->mem_start = pci_resource_start(pdev, 0);
+       mem_len = MB_GET_CID_ADDR(17);
+       dev->mem_end = dev->mem_start + mem_len;
+       dev->irq = pdev->irq;
+
+       bp->regview = ioremap_nocache(dev->base_addr, mem_len);
+
+       if (!bp->regview) {
+               printk(KERN_ERR PFX "Cannot map register space, aborting.\n");
+               rc = -ENOMEM;
+               goto err_out_release;
+       }
+
+       /* Configure byte swap and enable write to the reg_window registers.
+        * Rely on CPU to do target byte swapping on big endian systems
+        * The chip's target access swapping will not swap all accesses
+        */
+       pci_write_config_dword(bp->pdev, BNX2_PCICFG_MISC_CONFIG,
+                              BNX2_PCICFG_MISC_CONFIG_REG_WINDOW_ENA |
+                              BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP);
+
+       bnx2_set_power_state(bp, 0);
+
+       bp->chip_id = REG_RD(bp, BNX2_MISC_ID);
+
+       bp->phy_addr = 1;
+
+       /* Get bus information. */
+       reg = REG_RD(bp, BNX2_PCICFG_MISC_STATUS);
+       if (reg & BNX2_PCICFG_MISC_STATUS_PCIX_DET) {
+               u32 clkreg;
+
+               bp->flags |= PCIX_FLAG;
+
+               clkreg = REG_RD(bp, BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS);
+               
+               clkreg &= BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET;
+               switch (clkreg) {
+               case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_133MHZ:
+                       bp->bus_speed_mhz = 133;
+                       break;
+
+               case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_95MHZ:
+                       bp->bus_speed_mhz = 100;
+                       break;
+
+               case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_66MHZ:
+               case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_80MHZ:
+                       bp->bus_speed_mhz = 66;
+                       break;
+
+               case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_48MHZ:
+               case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_55MHZ:
+                       bp->bus_speed_mhz = 50;
+                       break;
+
+               case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_LOW:
+               case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_32MHZ:
+               case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_38MHZ:
+                       bp->bus_speed_mhz = 33;
+                       break;
+               }
+       }
+       else {
+               if (reg & BNX2_PCICFG_MISC_STATUS_M66EN)
+                       bp->bus_speed_mhz = 66;
+               else
+                       bp->bus_speed_mhz = 33;
+       }
+
+       if (reg & BNX2_PCICFG_MISC_STATUS_32BIT_DET)
+               bp->flags |= PCI_32BIT_FLAG;
+
+       /* 5706A0 may falsely detect SERR and PERR. */
+       if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
+               reg = REG_RD(bp, PCI_COMMAND);
+               reg &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+               REG_WR(bp, PCI_COMMAND, reg);
+       }
+       else if ((CHIP_ID(bp) == CHIP_ID_5706_A1) &&
+               !(bp->flags & PCIX_FLAG)) {
+
+               printk(KERN_ERR PFX "5706 A1 can only be used in a PCIX bus, "
+                      "aborting.\n");
+               goto err_out_unmap;
+       }
+
+       bnx2_init_nvram(bp);
+
+       /* Get the permanent MAC address.  First we need to make sure the
+        * firmware is actually running.
+        */
+       reg = REG_RD_IND(bp, HOST_VIEW_SHMEM_BASE + BNX2_DEV_INFO_SIGNATURE);
+
+       if ((reg & BNX2_DEV_INFO_SIGNATURE_MAGIC_MASK) !=
+           BNX2_DEV_INFO_SIGNATURE_MAGIC) {
+               printk(KERN_ERR PFX "Firmware not running, aborting.\n");
+               rc = -ENODEV;
+               goto err_out_unmap;
+       }
+
+       bp->fw_ver = REG_RD_IND(bp, HOST_VIEW_SHMEM_BASE +
+                               BNX2_DEV_INFO_BC_REV);
+
+       reg = REG_RD_IND(bp, HOST_VIEW_SHMEM_BASE + BNX2_PORT_HW_CFG_MAC_UPPER);
+       bp->mac_addr[0] = (u8) (reg >> 8);
+       bp->mac_addr[1] = (u8) reg;
+
+       reg = REG_RD_IND(bp, HOST_VIEW_SHMEM_BASE + BNX2_PORT_HW_CFG_MAC_LOWER);
+       bp->mac_addr[2] = (u8) (reg >> 24);
+       bp->mac_addr[3] = (u8) (reg >> 16);
+       bp->mac_addr[4] = (u8) (reg >> 8);
+       bp->mac_addr[5] = (u8) reg;
+
+       bp->tx_ring_size = MAX_TX_DESC_CNT;
+       bp->rx_ring_size = 100;
+
+       bp->rx_csum = 1;
+
+       bp->rx_offset = sizeof(struct l2_fhdr) + 2;
+
+       bp->tx_quick_cons_trip_int = 20;
+       bp->tx_quick_cons_trip = 20;
+       bp->tx_ticks_int = 80;
+       bp->tx_ticks = 80;
+               
+       bp->rx_quick_cons_trip_int = 6;
+       bp->rx_quick_cons_trip = 6;
+       bp->rx_ticks_int = 18;
+       bp->rx_ticks = 18;
+
+       bp->stats_ticks = 1000000 & 0xffff00;
+
+       bp->timer_interval =  HZ;
+
+       /* Disable WOL support if we are running on a SERDES chip. */
+       if (CHIP_BOND_ID(bp) & CHIP_BOND_ID_SERDES_BIT) {
+               bp->phy_flags |= PHY_SERDES_FLAG;
+               bp->flags |= NO_WOL_FLAG;
+       }
+
+       if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
+               bp->tx_quick_cons_trip_int =
+                       bp->tx_quick_cons_trip;
+               bp->tx_ticks_int = bp->tx_ticks;
+               bp->rx_quick_cons_trip_int =
+                       bp->rx_quick_cons_trip;
+               bp->rx_ticks_int = bp->rx_ticks;
+               bp->comp_prod_trip_int = bp->comp_prod_trip;
+               bp->com_ticks_int = bp->com_ticks;
+               bp->cmd_ticks_int = bp->cmd_ticks;
+       }
+
+       bp->autoneg = AUTONEG_SPEED | AUTONEG_FLOW_CTRL;
+       bp->req_line_speed = 0;
+       if (bp->phy_flags & PHY_SERDES_FLAG) {
+               bp->advertising = ETHTOOL_ALL_FIBRE_SPEED | ADVERTISED_Autoneg;
+       }
+       else {
+               bp->advertising = ETHTOOL_ALL_COPPER_SPEED | ADVERTISED_Autoneg;
+       }
+
+       bp->req_flow_ctrl = FLOW_CTRL_RX | FLOW_CTRL_TX;
+
+       return 0;
+
+err_out_unmap:
+       if (bp->regview) {
+               iounmap(bp->regview);
+       }
+
+err_out_release:
+       pci_release_regions(pdev);
+
+err_out_disable:
+       pci_disable_device(pdev);
+       pci_set_drvdata(pdev, NULL);
+
+err_out:
+       return rc;
+}
+
+static int __devinit
+bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       static int version_printed = 0;
+       struct net_device *dev = NULL;
+       struct bnx2 *bp;
+       int rc, i;
+
+       if (version_printed++ == 0)
+               printk(KERN_INFO "%s", version);
+
+       /* dev zeroed in init_etherdev */
+       dev = alloc_etherdev(sizeof(*bp));
+
+       if (!dev)
+               return -ENOMEM;
+
+       rc = bnx2_init_board(pdev, dev);
+       if (rc < 0) {
+               free_netdev(dev);
+               return rc;
+       }
+
+       dev->open = bnx2_open;
+       dev->hard_start_xmit = bnx2_start_xmit;
+       dev->stop = bnx2_close;
+       dev->get_stats = bnx2_get_stats;
+       dev->set_multicast_list = bnx2_set_rx_mode;
+       dev->do_ioctl = bnx2_ioctl;
+       dev->set_mac_address = bnx2_change_mac_addr;
+       dev->change_mtu = bnx2_change_mtu;
+       dev->tx_timeout = bnx2_tx_timeout;
+       dev->watchdog_timeo = TX_TIMEOUT;
+#ifdef BCM_VLAN
+       dev->vlan_rx_register = bnx2_vlan_rx_register;
+       dev->vlan_rx_kill_vid = bnx2_vlan_rx_kill_vid;
+#endif
+       dev->poll = bnx2_poll;
+       dev->ethtool_ops = &bnx2_ethtool_ops;
+       dev->weight = 64;
+
+       bp = dev->priv;
+
+#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER)
+       dev->poll_controller = poll_bnx2;
+#endif
+
+       if ((rc = register_netdev(dev))) {
+               printk(KERN_ERR PFX "Cannot register net device\n");
+               if (bp->regview)
+                       iounmap(bp->regview);
+               pci_release_regions(pdev);
+               pci_disable_device(pdev);
+               pci_set_drvdata(pdev, NULL);
+               free_netdev(dev);
+               return rc;
+       }
+
+       pci_set_drvdata(pdev, dev);
+
+       memcpy(dev->dev_addr, bp->mac_addr, 6);
+       bp->name = board_info[ent->driver_data].name,
+       printk(KERN_INFO "%s: %s (%c%d) PCI%s %s %dMHz found at mem %lx, "
+               "IRQ %d, ",
+               dev->name,
+               bp->name,
+               ((CHIP_ID(bp) & 0xf000) >> 12) + 'A',
+               ((CHIP_ID(bp) & 0x0ff0) >> 4),
+               ((bp->flags & PCIX_FLAG) ? "-X" : ""),
+               ((bp->flags & PCI_32BIT_FLAG) ? "32-bit" : "64-bit"),
+               bp->bus_speed_mhz,
+               dev->base_addr,
+               bp->pdev->irq);
+
+       printk("node addr ");
+       for (i = 0; i < 6; i++)
+               printk("%2.2x", dev->dev_addr[i]);
+       printk("\n");
+
+       dev->features |= NETIF_F_SG;
+       if (bp->flags & USING_DAC_FLAG)
+               dev->features |= NETIF_F_HIGHDMA;
+       dev->features |= NETIF_F_IP_CSUM;
+#ifdef BCM_VLAN
+       dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+#endif
+#ifdef BCM_TSO
+       dev->features |= NETIF_F_TSO;
+#endif
+
+       netif_carrier_off(bp->dev);
+
+       return 0;
+}
+
+static void __devexit
+bnx2_remove_one(struct pci_dev *pdev)
+{
+       struct net_device *dev = pci_get_drvdata(pdev);
+       struct bnx2 *bp = dev->priv;
+
+       unregister_netdev(dev);
+
+       if (bp->regview)
+               iounmap(bp->regview);
+
+       free_netdev(dev);
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+       pci_set_drvdata(pdev, NULL);
+}
+
+static int
+bnx2_suspend(struct pci_dev *pdev, u32 state)
+{
+       struct net_device *dev = pci_get_drvdata(pdev);
+       struct bnx2 *bp = dev->priv;
+       u32 reset_code;
+
+       if (!netif_running(dev))
+               return 0;
+
+       bnx2_netif_stop(bp);
+       netif_device_detach(dev);
+       del_timer_sync(&bp->timer);
+       if (bp->wol)
+               reset_code = BNX2_DRV_MSG_CODE_SUSPEND_WOL;
+       else
+               reset_code = BNX2_DRV_MSG_CODE_SUSPEND_NO_WOL;
+       bnx2_reset_chip(bp, reset_code);
+       bnx2_free_skbs(bp);
+       bnx2_set_power_state(bp, state);
+       return 0;
+}
+
+static int
+bnx2_resume(struct pci_dev *pdev)
+{
+       struct net_device *dev = pci_get_drvdata(pdev);
+       struct bnx2 *bp = dev->priv;
+
+       if (!netif_running(dev))
+               return 0;
+
+       bnx2_set_power_state(bp, 0);
+       netif_device_attach(dev);
+       bnx2_init_nic(bp);
+       bnx2_netif_start(bp);
+       return 0;
+}
+
+static struct pci_driver bnx2_pci_driver = {
+       name:           DRV_MODULE_NAME,
+       id_table:       bnx2_pci_tbl,
+       probe:          bnx2_init_one,
+       remove:         __devexit_p(bnx2_remove_one),
+       suspend:        bnx2_suspend,
+       resume:         bnx2_resume,
+};
+
+static int __init bnx2_init(void)
+{
+       return pci_module_init(&bnx2_pci_driver);
+}
+
+static void __exit bnx2_cleanup(void)
+{
+       pci_unregister_driver(&bnx2_pci_driver);
+}
+
+module_init(bnx2_init);
+module_exit(bnx2_cleanup);
+
+
+
diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h
new file mode 100644 (file)
index 0000000..8214a28
--- /dev/null
@@ -0,0 +1,4352 @@
+/* bnx2.h: Broadcom NX2 network driver.
+ *
+ * Copyright (c) 2004, 2005 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.
+ *
+ * Written by: Michael Chan  (mchan@broadcom.com)
+ */
+
+
+#ifndef BNX2_H
+#define BNX2_H
+
+#include <linux/config.h>
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/dma-mapping.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+#include <linux/time.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#ifdef NETIF_F_HW_VLAN_TX
+#include <linux/if_vlan.h>
+#define BCM_VLAN 1
+#endif
+#ifdef NETIF_F_TSO
+#include <net/ip.h>
+#include <net/tcp.h>
+#include <net/checksum.h>
+#define BCM_TSO 1
+#endif
+#include <linux/workqueue.h>
+#include <linux/crc32.h>
+
+/* Hardware data structures and register definitions automatically
+ * generated from RTL code. Do not modify.
+ */
+
+/*
+ *  tx_bd definition
+ */
+struct tx_bd {
+       u32 tx_bd_haddr_hi;
+       u32 tx_bd_haddr_lo;                                   
+       u32 tx_bd_mss_nbytes;                                     
+       u32 tx_bd_vlan_tag_flags;                                      
+               #define TX_BD_FLAGS_CONN_FAULT          (1<<0)
+               #define TX_BD_FLAGS_TCP_UDP_CKSUM       (1<<1)
+               #define TX_BD_FLAGS_IP_CKSUM            (1<<2)
+               #define TX_BD_FLAGS_VLAN_TAG            (1<<3)
+               #define TX_BD_FLAGS_COAL_NOW            (1<<4)
+               #define TX_BD_FLAGS_DONT_GEN_CRC        (1<<5)
+               #define TX_BD_FLAGS_END                 (1<<6)
+               #define TX_BD_FLAGS_START               (1<<7)
+               #define TX_BD_FLAGS_SW_OPTION_WORD      (0x1f<<8)
+               #define TX_BD_FLAGS_SW_FLAGS            (1<<13)
+               #define TX_BD_FLAGS_SW_SNAP             (1<<14)
+               #define TX_BD_FLAGS_SW_LSO              (1<<15)
+
+};
+
+
+/*
+ *  rx_bd definition
+ */
+struct rx_bd {
+       u32 rx_bd_haddr_hi;
+       u32 rx_bd_haddr_lo;
+       u32 rx_bd_len;
+       u32 rx_bd_flags;
+               #define RX_BD_FLAGS_NOPUSH              (1<<0)
+               #define RX_BD_FLAGS_DUMMY               (1<<1)
+               #define RX_BD_FLAGS_END                 (1<<2)
+               #define RX_BD_FLAGS_START               (1<<3)
+
+};
+
+
+/*
+ *  status_block definition
+ */
+struct status_block {
+       u32 status_attn_bits;
+               #define STATUS_ATTN_BITS_LINK_STATE             (1L<<0)
+               #define STATUS_ATTN_BITS_TX_SCHEDULER_ABORT     (1L<<1)
+               #define STATUS_ATTN_BITS_TX_BD_READ_ABORT       (1L<<2)
+               #define STATUS_ATTN_BITS_TX_BD_CACHE_ABORT      (1L<<3)
+               #define STATUS_ATTN_BITS_TX_PROCESSOR_ABORT     (1L<<4)
+               #define STATUS_ATTN_BITS_TX_DMA_ABORT           (1L<<5)
+               #define STATUS_ATTN_BITS_TX_PATCHUP_ABORT       (1L<<6)
+               #define STATUS_ATTN_BITS_TX_ASSEMBLER_ABORT     (1L<<7)
+               #define STATUS_ATTN_BITS_RX_PARSER_MAC_ABORT    (1L<<8)
+               #define STATUS_ATTN_BITS_RX_PARSER_CATCHUP_ABORT        (1L<<9)
+               #define STATUS_ATTN_BITS_RX_MBUF_ABORT          (1L<<10)
+               #define STATUS_ATTN_BITS_RX_LOOKUP_ABORT        (1L<<11)
+               #define STATUS_ATTN_BITS_RX_PROCESSOR_ABORT     (1L<<12)
+               #define STATUS_ATTN_BITS_RX_V2P_ABORT           (1L<<13)
+               #define STATUS_ATTN_BITS_RX_BD_CACHE_ABORT      (1L<<14)
+               #define STATUS_ATTN_BITS_RX_DMA_ABORT           (1L<<15)
+               #define STATUS_ATTN_BITS_COMPLETION_ABORT       (1L<<16)
+               #define STATUS_ATTN_BITS_HOST_COALESCE_ABORT    (1L<<17)
+               #define STATUS_ATTN_BITS_MAILBOX_QUEUE_ABORT    (1L<<18)
+               #define STATUS_ATTN_BITS_CONTEXT_ABORT          (1L<<19)
+               #define STATUS_ATTN_BITS_CMD_SCHEDULER_ABORT    (1L<<20)
+               #define STATUS_ATTN_BITS_CMD_PROCESSOR_ABORT    (1L<<21)
+               #define STATUS_ATTN_BITS_MGMT_PROCESSOR_ABORT   (1L<<22)
+               #define STATUS_ATTN_BITS_MAC_ABORT              (1L<<23)
+               #define STATUS_ATTN_BITS_TIMER_ABORT            (1L<<24)
+               #define STATUS_ATTN_BITS_DMAE_ABORT             (1L<<25)
+               #define STATUS_ATTN_BITS_FLSH_ABORT             (1L<<26)
+               #define STATUS_ATTN_BITS_GRC_ABORT              (1L<<27)
+               #define STATUS_ATTN_BITS_PARITY_ERROR           (1L<<31)
+
+       u32 status_attn_bits_ack;
+#if defined(__BIG_ENDIAN)
+       u16 status_tx_quick_consumer_index0;
+       u16 status_tx_quick_consumer_index1;
+       u16 status_tx_quick_consumer_index2;
+       u16 status_tx_quick_consumer_index3;
+       u16 status_rx_quick_consumer_index0;
+       u16 status_rx_quick_consumer_index1;
+       u16 status_rx_quick_consumer_index2;
+       u16 status_rx_quick_consumer_index3;
+       u16 status_rx_quick_consumer_index4;
+       u16 status_rx_quick_consumer_index5;
+       u16 status_rx_quick_consumer_index6;
+       u16 status_rx_quick_consumer_index7;
+       u16 status_rx_quick_consumer_index8;
+       u16 status_rx_quick_consumer_index9;
+       u16 status_rx_quick_consumer_index10;
+       u16 status_rx_quick_consumer_index11;
+       u16 status_rx_quick_consumer_index12;
+       u16 status_rx_quick_consumer_index13;
+       u16 status_rx_quick_consumer_index14;
+       u16 status_rx_quick_consumer_index15;
+       u16 status_completion_producer_index;
+       u16 status_cmd_consumer_index;
+       u16 status_idx;
+       u16 status_unused;
+#elif defined(__LITTLE_ENDIAN)
+       u16 status_tx_quick_consumer_index1;
+       u16 status_tx_quick_consumer_index0;
+       u16 status_tx_quick_consumer_index3;
+       u16 status_tx_quick_consumer_index2;
+       u16 status_rx_quick_consumer_index1;
+       u16 status_rx_quick_consumer_index0;
+       u16 status_rx_quick_consumer_index3;
+       u16 status_rx_quick_consumer_index2;
+       u16 status_rx_quick_consumer_index5;
+       u16 status_rx_quick_consumer_index4;
+       u16 status_rx_quick_consumer_index7;
+       u16 status_rx_quick_consumer_index6;
+       u16 status_rx_quick_consumer_index9;
+       u16 status_rx_quick_consumer_index8;
+       u16 status_rx_quick_consumer_index11;
+       u16 status_rx_quick_consumer_index10;
+       u16 status_rx_quick_consumer_index13;
+       u16 status_rx_quick_consumer_index12;
+       u16 status_rx_quick_consumer_index15;
+       u16 status_rx_quick_consumer_index14;
+       u16 status_cmd_consumer_index;
+       u16 status_completion_producer_index;
+       u16 status_unused;
+       u16 status_idx;
+#endif
+};
+
+
+/*
+ *  statistics_block definition
+ */
+struct statistics_block {
+       u32 stat_IfHCInOctets_hi;
+       u32 stat_IfHCInOctets_lo;
+       u32 stat_IfHCInBadOctets_hi;
+       u32 stat_IfHCInBadOctets_lo;
+       u32 stat_IfHCOutOctets_hi;
+       u32 stat_IfHCOutOctets_lo;
+       u32 stat_IfHCOutBadOctets_hi;
+       u32 stat_IfHCOutBadOctets_lo;
+       u32 stat_IfHCInUcastPkts_hi;
+       u32 stat_IfHCInUcastPkts_lo;
+       u32 stat_IfHCInMulticastPkts_hi;
+       u32 stat_IfHCInMulticastPkts_lo;
+       u32 stat_IfHCInBroadcastPkts_hi;
+       u32 stat_IfHCInBroadcastPkts_lo;
+       u32 stat_IfHCOutUcastPkts_hi;
+       u32 stat_IfHCOutUcastPkts_lo;
+       u32 stat_IfHCOutMulticastPkts_hi;
+       u32 stat_IfHCOutMulticastPkts_lo;
+       u32 stat_IfHCOutBroadcastPkts_hi;
+       u32 stat_IfHCOutBroadcastPkts_lo;
+       u32 stat_emac_tx_stat_dot3statsinternalmactransmiterrors;
+       u32 stat_Dot3StatsCarrierSenseErrors;
+       u32 stat_Dot3StatsFCSErrors;
+       u32 stat_Dot3StatsAlignmentErrors;
+       u32 stat_Dot3StatsSingleCollisionFrames;
+       u32 stat_Dot3StatsMultipleCollisionFrames;
+       u32 stat_Dot3StatsDeferredTransmissions;
+       u32 stat_Dot3StatsExcessiveCollisions;
+       u32 stat_Dot3StatsLateCollisions;
+       u32 stat_EtherStatsCollisions;
+       u32 stat_EtherStatsFragments;
+       u32 stat_EtherStatsJabbers;
+       u32 stat_EtherStatsUndersizePkts;
+       u32 stat_EtherStatsOverrsizePkts;
+       u32 stat_EtherStatsPktsRx64Octets;
+       u32 stat_EtherStatsPktsRx65Octetsto127Octets;
+       u32 stat_EtherStatsPktsRx128Octetsto255Octets;
+       u32 stat_EtherStatsPktsRx256Octetsto511Octets;
+       u32 stat_EtherStatsPktsRx512Octetsto1023Octets;
+       u32 stat_EtherStatsPktsRx1024Octetsto1522Octets;
+       u32 stat_EtherStatsPktsRx1523Octetsto9022Octets;
+       u32 stat_EtherStatsPktsTx64Octets;
+       u32 stat_EtherStatsPktsTx65Octetsto127Octets;
+       u32 stat_EtherStatsPktsTx128Octetsto255Octets;
+       u32 stat_EtherStatsPktsTx256Octetsto511Octets;
+       u32 stat_EtherStatsPktsTx512Octetsto1023Octets;
+       u32 stat_EtherStatsPktsTx1024Octetsto1522Octets;
+       u32 stat_EtherStatsPktsTx1523Octetsto9022Octets;
+       u32 stat_XonPauseFramesReceived;
+       u32 stat_XoffPauseFramesReceived;
+       u32 stat_OutXonSent;
+       u32 stat_OutXoffSent;
+       u32 stat_FlowControlDone;
+       u32 stat_MacControlFramesReceived;
+       u32 stat_XoffStateEntered;
+       u32 stat_IfInFramesL2FilterDiscards;
+       u32 stat_IfInRuleCheckerDiscards;
+       u32 stat_IfInFTQDiscards;
+       u32 stat_IfInMBUFDiscards;
+       u32 stat_IfInRuleCheckerP4Hit;
+       u32 stat_CatchupInRuleCheckerDiscards;
+       u32 stat_CatchupInFTQDiscards;
+       u32 stat_CatchupInMBUFDiscards;
+       u32 stat_CatchupInRuleCheckerP4Hit;
+       u32 stat_GenStat00;
+       u32 stat_GenStat01;
+       u32 stat_GenStat02;
+       u32 stat_GenStat03;
+       u32 stat_GenStat04;
+       u32 stat_GenStat05;
+       u32 stat_GenStat06;
+       u32 stat_GenStat07;
+       u32 stat_GenStat08;
+       u32 stat_GenStat09;
+       u32 stat_GenStat10;
+       u32 stat_GenStat11;
+       u32 stat_GenStat12;
+       u32 stat_GenStat13;
+       u32 stat_GenStat14;
+       u32 stat_GenStat15;
+};
+
+
+/*
+ *  l2_fhdr definition
+ */
+struct l2_fhdr {
+#if defined(__BIG_ENDIAN)
+       u16 l2_fhdr_errors;
+       u16 l2_fhdr_status;
+#elif defined(__LITTLE_ENDIAN)
+       u16 l2_fhdr_status;
+       u16 l2_fhdr_errors;
+#endif
+               #define L2_FHDR_ERRORS_BAD_CRC          (1<<1)
+               #define L2_FHDR_ERRORS_PHY_DECODE       (1<<2)
+               #define L2_FHDR_ERRORS_ALIGNMENT        (1<<3)
+               #define L2_FHDR_ERRORS_TOO_SHORT        (1<<4)
+               #define L2_FHDR_ERRORS_GIANT_FRAME      (1<<5)
+
+               #define L2_FHDR_STATUS_RULE_CLASS       (0x7<<0)
+               #define L2_FHDR_STATUS_RULE_P2          (1<<3)
+               #define L2_FHDR_STATUS_RULE_P3          (1<<4)
+               #define L2_FHDR_STATUS_RULE_P4          (1<<5)
+               #define L2_FHDR_STATUS_L2_VLAN_TAG      (1<<6)
+               #define L2_FHDR_STATUS_L2_LLC_SNAP      (1<<7)
+               #define L2_FHDR_STATUS_RSS_HASH         (1<<8)
+               #define L2_FHDR_STATUS_IP_DATAGRAM      (1<<13)
+               #define L2_FHDR_STATUS_TCP_SEGMENT      (1<<14)
+               #define L2_FHDR_STATUS_UDP_DATAGRAM     (1<<15)
+
+       u32 l2_fhdr_hash;
+#if defined(__BIG_ENDIAN)
+       u16 l2_fhdr_pkt_len;
+       u16 l2_fhdr_vlan_tag;
+       u16 l2_fhdr_ip_xsum;
+       u16 l2_fhdr_tcp_udp_xsum;
+#elif defined(__LITTLE_ENDIAN)
+       u16 l2_fhdr_vlan_tag;
+       u16 l2_fhdr_pkt_len;
+       u16 l2_fhdr_tcp_udp_xsum;
+       u16 l2_fhdr_ip_xsum;
+#endif
+};
+
+
+/*
+ *  l2_context definition
+ */
+#define BNX2_L2CTX_TYPE                                        0x00000000
+#define BNX2_L2CTX_TYPE_SIZE_L2                                 ((0xc0/0x20)<<16)
+#define BNX2_L2CTX_TYPE_TYPE                            (0xf<<28)
+#define BNX2_L2CTX_TYPE_TYPE_EMPTY                      (0<<28)
+#define BNX2_L2CTX_TYPE_TYPE_L2                                 (1<<28)
+
+#define BNX2_L2CTX_TX_HOST_BIDX                                0x00000088
+#define BNX2_L2CTX_EST_NBD                             0x00000088
+#define BNX2_L2CTX_CMD_TYPE                            0x00000088
+#define BNX2_L2CTX_CMD_TYPE_TYPE                        (0xf<<24)
+#define BNX2_L2CTX_CMD_TYPE_TYPE_L2                     (0<<24)
+#define BNX2_L2CTX_CMD_TYPE_TYPE_TCP                    (1<<24)
+
+#define BNX2_L2CTX_TX_HOST_BSEQ                                0x00000090
+#define BNX2_L2CTX_TSCH_BSEQ                           0x00000094
+#define BNX2_L2CTX_TBDR_BSEQ                           0x00000098
+#define BNX2_L2CTX_TBDR_BOFF                           0x0000009c
+#define BNX2_L2CTX_TBDR_BIDX                           0x0000009c
+#define BNX2_L2CTX_TBDR_BHADDR_HI                      0x000000a0
+#define BNX2_L2CTX_TBDR_BHADDR_LO                      0x000000a4
+#define BNX2_L2CTX_TXP_BOFF                            0x000000a8
+#define BNX2_L2CTX_TXP_BIDX                            0x000000a8
+#define BNX2_L2CTX_TXP_BSEQ                            0x000000ac
+
+
+/*
+ *  l2_bd_chain_context definition
+ */
+#define BNX2_L2CTX_BD_PRE_READ                         0x00000000
+#define BNX2_L2CTX_CTX_SIZE                            0x00000000
+#define BNX2_L2CTX_CTX_TYPE                            0x00000000
+#define BNX2_L2CTX_CTX_TYPE_SIZE_L2                     ((0x20/20)<<16)
+#define BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE             (0xf<<28)
+#define BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_UNDEFINED   (0<<28)
+#define BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE       (1<<28)
+
+#define BNX2_L2CTX_HOST_BDIDX                          0x00000004
+#define BNX2_L2CTX_HOST_BSEQ                           0x00000008
+#define BNX2_L2CTX_NX_BSEQ                             0x0000000c
+#define BNX2_L2CTX_NX_BDHADDR_HI                       0x00000010
+#define BNX2_L2CTX_NX_BDHADDR_LO                       0x00000014
+#define BNX2_L2CTX_NX_BDIDX                            0x00000018
+
+
+/*
+ *  pci_config_l definition
+ *  offset: 0000
+ */
+#define BNX2_PCICFG_MISC_CONFIG                                0x00000068
+#define BNX2_PCICFG_MISC_CONFIG_TARGET_BYTE_SWAP        (1L<<2)
+#define BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP     (1L<<3)
+#define BNX2_PCICFG_MISC_CONFIG_CLOCK_CTL_ENA           (1L<<5)
+#define BNX2_PCICFG_MISC_CONFIG_TARGET_GRC_WORD_SWAP    (1L<<6)
+#define BNX2_PCICFG_MISC_CONFIG_REG_WINDOW_ENA          (1L<<7)
+#define BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ            (1L<<8)
+#define BNX2_PCICFG_MISC_CONFIG_CORE_RST_BSY            (1L<<9)
+#define BNX2_PCICFG_MISC_CONFIG_ASIC_METAL_REV          (0xffL<<16)
+#define BNX2_PCICFG_MISC_CONFIG_ASIC_BASE_REV           (0xfL<<24)
+#define BNX2_PCICFG_MISC_CONFIG_ASIC_ID                         (0xfL<<28)
+
+#define BNX2_PCICFG_MISC_STATUS                                0x0000006c
+#define BNX2_PCICFG_MISC_STATUS_INTA_VALUE              (1L<<0)
+#define BNX2_PCICFG_MISC_STATUS_32BIT_DET               (1L<<1)
+#define BNX2_PCICFG_MISC_STATUS_M66EN                   (1L<<2)
+#define BNX2_PCICFG_MISC_STATUS_PCIX_DET                (1L<<3)
+#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED              (0x3L<<4)
+#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED_66           (0L<<4)
+#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED_100          (1L<<4)
+#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED_133          (2L<<4)
+#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED_PCI_MODE     (3L<<4)
+
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS             0x00000070
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET      (0xfL<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_32MHZ        (0L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_38MHZ        (1L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_48MHZ        (2L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_55MHZ        (3L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_66MHZ        (4L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_80MHZ        (5L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_95MHZ        (6L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_133MHZ       (7L<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_LOW  (0xfL<<0)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_DISABLE     (1L<<6)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT         (1L<<7)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC     (0x7L<<8)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_UNDEF       (0L<<8)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_12  (1L<<8)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_6   (2L<<8)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_62  (4L<<8)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PLAY_DEAD    (1L<<11)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED   (0xfL<<12)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_100       (0L<<12)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_80        (1L<<12)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_50        (2L<<12)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_40        (4L<<12)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_25        (8L<<12)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_STOP    (1L<<16)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_PLL_STOP         (1L<<17)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_RESERVED_18  (1L<<18)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_USE_SPD_DET  (1L<<19)
+#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_RESERVED     (0xfffL<<20)
+
+#define BNX2_PCICFG_REG_WINDOW_ADDRESS                 0x00000078
+#define BNX2_PCICFG_REG_WINDOW                         0x00000080
+#define BNX2_PCICFG_INT_ACK_CMD                                0x00000084
+#define BNX2_PCICFG_INT_ACK_CMD_INDEX                   (0xffffL<<0)
+#define BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID             (1L<<16)
+#define BNX2_PCICFG_INT_ACK_CMD_USE_INT_HC_PARAM        (1L<<17)
+#define BNX2_PCICFG_INT_ACK_CMD_MASK_INT                (1L<<18)
+
+#define BNX2_PCICFG_STATUS_BIT_SET_CMD                 0x00000088
+#define BNX2_PCICFG_STATUS_BIT_CLEAR_CMD               0x0000008c
+#define BNX2_PCICFG_MAILBOX_QUEUE_ADDR                 0x00000090
+#define BNX2_PCICFG_MAILBOX_QUEUE_DATA                 0x00000094
+
+
+/*
+ *  pci_reg definition
+ *  offset: 0x400
+ */
+#define BNX2_PCI_GRC_WINDOW_ADDR                       0x00000400
+#define BNX2_PCI_GRC_WINDOW_ADDR_PCI_GRC_WINDOW_ADDR_VALUE      (0x3ffffL<<8)
+
+#define BNX2_PCI_CONFIG_1                              0x00000404
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY                         (0x7L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_OFF             (0L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_16              (1L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_32              (2L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_64              (3L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_128             (4L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_256             (5L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_512             (6L<<8)
+#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_1024            (7L<<8)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY                (0x7L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_OFF            (0L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_16             (1L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_32             (2L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_64             (3L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_128            (4L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_256            (5L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_512            (6L<<11)
+#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_1024           (7L<<11)
+
+#define BNX2_PCI_CONFIG_2                              0x00000408
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE                     (0xfL<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_DISABLED            (0L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_64K                         (1L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_128K                (2L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_256K                (3L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_512K                (4L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_1M                  (5L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_2M                  (6L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_4M                  (7L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_8M                  (8L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_16M                         (9L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_32M                         (10L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_64M                         (11L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_128M                (12L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_256M                (13L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_512M                (14L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_SIZE_1G                  (15L<<0)
+#define BNX2_PCI_CONFIG_2_BAR1_64ENA                    (1L<<4)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_RETRY                         (1L<<5)
+#define BNX2_PCI_CONFIG_2_CFG_CYCLE_RETRY               (1L<<6)
+#define BNX2_PCI_CONFIG_2_FIRST_CFG_DONE                (1L<<7)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE                  (0xffL<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_DISABLED                 (0L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_1K               (1L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_2K               (2L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_4K               (3L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_8K               (4L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_16K              (5L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_32K              (6L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_64K              (7L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_128K             (8L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_256K             (9L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_512K             (10L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_1M               (11L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_2M               (12L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_4M               (13L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_8M               (14L<<8)
+#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_16M              (15L<<8)
+#define BNX2_PCI_CONFIG_2_MAX_SPLIT_LIMIT               (0x1fL<<16)
+#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT                (0x3L<<21)
+#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT_512            (0L<<21)
+#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT_1K             (1L<<21)
+#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT_2K             (2L<<21)
+#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT_4K             (3L<<21)
+#define BNX2_PCI_CONFIG_2_FORCE_32_BIT_MSTR             (1L<<23)
+#define BNX2_PCI_CONFIG_2_FORCE_32_BIT_TGT              (1L<<24)
+#define BNX2_PCI_CONFIG_2_KEEP_REQ_ASSERT               (1L<<25)
+
+#define BNX2_PCI_CONFIG_3                              0x0000040c
+#define BNX2_PCI_CONFIG_3_STICKY_BYTE                   (0xffL<<0)
+#define BNX2_PCI_CONFIG_3_FORCE_PME                     (1L<<24)
+#define BNX2_PCI_CONFIG_3_PME_STATUS                    (1L<<25)
+#define BNX2_PCI_CONFIG_3_PME_ENABLE                    (1L<<26)
+#define BNX2_PCI_CONFIG_3_PM_STATE                      (0x3L<<27)
+#define BNX2_PCI_CONFIG_3_VAUX_PRESET                   (1L<<30)
+#define BNX2_PCI_CONFIG_3_PCI_POWER                     (1L<<31)
+
+#define BNX2_PCI_PM_DATA_A                             0x00000410
+#define BNX2_PCI_PM_DATA_A_PM_DATA_0_PRG                (0xffL<<0)
+#define BNX2_PCI_PM_DATA_A_PM_DATA_1_PRG                (0xffL<<8)
+#define BNX2_PCI_PM_DATA_A_PM_DATA_2_PRG                (0xffL<<16)
+#define BNX2_PCI_PM_DATA_A_PM_DATA_3_PRG                (0xffL<<24)
+
+#define BNX2_PCI_PM_DATA_B                             0x00000414
+#define BNX2_PCI_PM_DATA_B_PM_DATA_4_PRG                (0xffL<<0)
+#define BNX2_PCI_PM_DATA_B_PM_DATA_5_PRG                (0xffL<<8)
+#define BNX2_PCI_PM_DATA_B_PM_DATA_6_PRG                (0xffL<<16)
+#define BNX2_PCI_PM_DATA_B_PM_DATA_7_PRG                (0xffL<<24)
+
+#define BNX2_PCI_SWAP_DIAG0                            0x00000418
+#define BNX2_PCI_SWAP_DIAG1                            0x0000041c
+#define BNX2_PCI_EXP_ROM_ADDR                          0x00000420
+#define BNX2_PCI_EXP_ROM_ADDR_ADDRESS                   (0x3fffffL<<2)
+#define BNX2_PCI_EXP_ROM_ADDR_REQ                       (1L<<31)
+
+#define BNX2_PCI_EXP_ROM_DATA                          0x00000424
+#define BNX2_PCI_VPD_INTF                              0x00000428
+#define BNX2_PCI_VPD_INTF_INTF_REQ                      (1L<<0)
+
+#define BNX2_PCI_VPD_ADDR_FLAG                         0x0000042c
+#define BNX2_PCI_VPD_ADDR_FLAG_ADDRESS                  (0x1fff<<2)
+#define BNX2_PCI_VPD_ADDR_FLAG_WR                       (1<<15)
+
+#define BNX2_PCI_VPD_DATA                              0x00000430
+#define BNX2_PCI_ID_VAL1                               0x00000434
+#define BNX2_PCI_ID_VAL1_DEVICE_ID                      (0xffffL<<0)
+#define BNX2_PCI_ID_VAL1_VENDOR_ID                      (0xffffL<<16)
+
+#define BNX2_PCI_ID_VAL2                               0x00000438
+#define BNX2_PCI_ID_VAL2_SUBSYSTEM_VENDOR_ID            (0xffffL<<0)
+#define BNX2_PCI_ID_VAL2_SUBSYSTEM_ID                   (0xffffL<<16)
+
+#define BNX2_PCI_ID_VAL3                               0x0000043c
+#define BNX2_PCI_ID_VAL3_CLASS_CODE                     (0xffffffL<<0)
+#define BNX2_PCI_ID_VAL3_REVISION_ID                    (0xffL<<24)
+
+#define BNX2_PCI_ID_VAL4                               0x00000440
+#define BNX2_PCI_ID_VAL4_CAP_ENA                        (0xfL<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_0                      (0L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_1                      (1L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_2                      (2L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_3                      (3L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_4                      (4L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_5                      (5L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_6                      (6L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_7                      (7L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_8                      (8L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_9                      (9L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_10                     (10L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_11                     (11L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_12                     (12L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_13                     (13L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_14                     (14L<<0)
+#define BNX2_PCI_ID_VAL4_CAP_ENA_15                     (15L<<0)
+#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG                   (0x3L<<6)
+#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG_0                         (0L<<6)
+#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG_1                         (1L<<6)
+#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG_2                         (2L<<6)
+#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG_3                         (3L<<6)
+#define BNX2_PCI_ID_VAL4_MSI_LIMIT                      (0x7L<<9)
+#define BNX2_PCI_ID_VAL4_MSI_ADVERTIZE                  (0x7L<<12)
+#define BNX2_PCI_ID_VAL4_MSI_ENABLE                     (1L<<15)
+#define BNX2_PCI_ID_VAL4_MAX_64_ADVERTIZE               (1L<<16)
+#define BNX2_PCI_ID_VAL4_MAX_133_ADVERTIZE              (1L<<17)
+#define BNX2_PCI_ID_VAL4_MAX_MEM_READ_SIZE              (0x3L<<21)
+#define BNX2_PCI_ID_VAL4_MAX_SPLIT_SIZE                         (0x7L<<23)
+#define BNX2_PCI_ID_VAL4_MAX_CUMULATIVE_SIZE            (0x7L<<26)
+
+#define BNX2_PCI_ID_VAL5                               0x00000444
+#define BNX2_PCI_ID_VAL5_D1_SUPPORT                     (1L<<0)
+#define BNX2_PCI_ID_VAL5_D2_SUPPORT                     (1L<<1)
+#define BNX2_PCI_ID_VAL5_PME_IN_D0                      (1L<<2)
+#define BNX2_PCI_ID_VAL5_PME_IN_D1                      (1L<<3)
+#define BNX2_PCI_ID_VAL5_PME_IN_D2                      (1L<<4)
+#define BNX2_PCI_ID_VAL5_PME_IN_D3_HOT                  (1L<<5)
+
+#define BNX2_PCI_PCIX_EXTENDED_STATUS                  0x00000448
+#define BNX2_PCI_PCIX_EXTENDED_STATUS_NO_SNOOP          (1L<<8)
+#define BNX2_PCI_PCIX_EXTENDED_STATUS_LONG_BURST        (1L<<9)
+#define BNX2_PCI_PCIX_EXTENDED_STATUS_SPLIT_COMP_MSG_CLASS      (0xfL<<16)
+#define BNX2_PCI_PCIX_EXTENDED_STATUS_SPLIT_COMP_MSG_IDX        (0xffL<<24)
+
+#define BNX2_PCI_ID_VAL6                               0x0000044c
+#define BNX2_PCI_ID_VAL6_MAX_LAT                        (0xffL<<0)
+#define BNX2_PCI_ID_VAL6_MIN_GNT                        (0xffL<<8)
+#define BNX2_PCI_ID_VAL6_BIST                           (0xffL<<16)
+
+#define BNX2_PCI_MSI_DATA                              0x00000450
+#define BNX2_PCI_MSI_DATA_PCI_MSI_DATA                  (0xffffL<<0)
+
+#define BNX2_PCI_MSI_ADDR_H                            0x00000454
+#define BNX2_PCI_MSI_ADDR_L                            0x00000458
+
+
+/*
+ *  misc_reg definition
+ *  offset: 0x800
+ */
+#define BNX2_MISC_COMMAND                              0x00000800
+#define BNX2_MISC_COMMAND_ENABLE_ALL                    (1L<<0)
+#define BNX2_MISC_COMMAND_DISABLE_ALL                   (1L<<1)
+#define BNX2_MISC_COMMAND_CORE_RESET                    (1L<<4)
+#define BNX2_MISC_COMMAND_HARD_RESET                    (1L<<5)
+#define BNX2_MISC_COMMAND_PAR_ERROR                     (1L<<8)
+#define BNX2_MISC_COMMAND_PAR_ERR_RAM                   (0x7fL<<16)
+
+#define BNX2_MISC_CFG                                  0x00000804
+#define BNX2_MISC_CFG_PCI_GRC_TMOUT                     (1L<<0)
+#define BNX2_MISC_CFG_NVM_WR_EN                                 (0x3L<<1)
+#define BNX2_MISC_CFG_NVM_WR_EN_PROTECT                         (0L<<1)
+#define BNX2_MISC_CFG_NVM_WR_EN_PCI                     (1L<<1)
+#define BNX2_MISC_CFG_NVM_WR_EN_ALLOW                   (2L<<1)
+#define BNX2_MISC_CFG_NVM_WR_EN_ALLOW2                  (3L<<1)
+#define BNX2_MISC_CFG_BIST_EN                           (1L<<3)
+#define BNX2_MISC_CFG_CK25_OUT_ALT_SRC                  (1L<<4)
+#define BNX2_MISC_CFG_BYPASS_BSCAN                      (1L<<5)
+#define BNX2_MISC_CFG_BYPASS_EJTAG                      (1L<<6)
+#define BNX2_MISC_CFG_CLK_CTL_OVERRIDE                  (1L<<7)
+#define BNX2_MISC_CFG_LEDMODE                           (0x3L<<8)
+#define BNX2_MISC_CFG_LEDMODE_MAC                       (0L<<8)
+#define BNX2_MISC_CFG_LEDMODE_GPHY1                     (1L<<8)
+#define BNX2_MISC_CFG_LEDMODE_GPHY2                     (2L<<8)
+
+#define BNX2_MISC_ID                                   0x00000808
+#define BNX2_MISC_ID_BOND_ID                            (0xfL<<0)
+#define BNX2_MISC_ID_CHIP_METAL                                 (0xffL<<4)
+#define BNX2_MISC_ID_CHIP_REV                           (0xfL<<12)
+#define BNX2_MISC_ID_CHIP_NUM                           (0xffffL<<16)
+
+#define BNX2_MISC_ENABLE_STATUS_BITS                   0x0000080c
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_SCHEDULER_ENABLE        (1L<<0)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_BD_READ_ENABLE  (1L<<1)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_BD_CACHE_ENABLE         (1L<<2)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_PROCESSOR_ENABLE        (1L<<3)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_DMA_ENABLE      (1L<<4)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_PATCHUP_ENABLE  (1L<<5)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_PAYLOAD_Q_ENABLE        (1L<<6)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_HEADER_Q_ENABLE         (1L<<7)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TX_ASSEMBLER_ENABLE        (1L<<8)
+#define BNX2_MISC_ENABLE_STATUS_BITS_EMAC_ENABLE        (1L<<9)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_PARSER_MAC_ENABLE       (1L<<10)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_PARSER_CATCHUP_ENABLE   (1L<<11)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_MBUF_ENABLE     (1L<<12)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_LOOKUP_ENABLE   (1L<<13)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_PROCESSOR_ENABLE        (1L<<14)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_V2P_ENABLE      (1L<<15)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_BD_CACHE_ENABLE         (1L<<16)
+#define BNX2_MISC_ENABLE_STATUS_BITS_RX_DMA_ENABLE      (1L<<17)
+#define BNX2_MISC_ENABLE_STATUS_BITS_COMPLETION_ENABLE  (1L<<18)
+#define BNX2_MISC_ENABLE_STATUS_BITS_HOST_COALESCE_ENABLE       (1L<<19)
+#define BNX2_MISC_ENABLE_STATUS_BITS_MAILBOX_QUEUE_ENABLE       (1L<<20)
+#define BNX2_MISC_ENABLE_STATUS_BITS_CONTEXT_ENABLE     (1L<<21)
+#define BNX2_MISC_ENABLE_STATUS_BITS_CMD_SCHEDULER_ENABLE       (1L<<22)
+#define BNX2_MISC_ENABLE_STATUS_BITS_CMD_PROCESSOR_ENABLE       (1L<<23)
+#define BNX2_MISC_ENABLE_STATUS_BITS_MGMT_PROCESSOR_ENABLE      (1L<<24)
+#define BNX2_MISC_ENABLE_STATUS_BITS_TIMER_ENABLE       (1L<<25)
+#define BNX2_MISC_ENABLE_STATUS_BITS_DMA_ENGINE_ENABLE  (1L<<26)
+#define BNX2_MISC_ENABLE_STATUS_BITS_UMP_ENABLE                 (1L<<27)
+
+#define BNX2_MISC_ENABLE_SET_BITS                      0x00000810
+#define BNX2_MISC_ENABLE_SET_BITS_TX_SCHEDULER_ENABLE   (1L<<0)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_BD_READ_ENABLE     (1L<<1)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_BD_CACHE_ENABLE    (1L<<2)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_PROCESSOR_ENABLE   (1L<<3)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_DMA_ENABLE                 (1L<<4)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_PATCHUP_ENABLE     (1L<<5)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_PAYLOAD_Q_ENABLE   (1L<<6)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_HEADER_Q_ENABLE    (1L<<7)
+#define BNX2_MISC_ENABLE_SET_BITS_TX_ASSEMBLER_ENABLE   (1L<<8)
+#define BNX2_MISC_ENABLE_SET_BITS_EMAC_ENABLE           (1L<<9)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_PARSER_MAC_ENABLE  (1L<<10)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_PARSER_CATCHUP_ENABLE      (1L<<11)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_MBUF_ENABLE        (1L<<12)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_LOOKUP_ENABLE      (1L<<13)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_PROCESSOR_ENABLE   (1L<<14)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_V2P_ENABLE                 (1L<<15)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_BD_CACHE_ENABLE    (1L<<16)
+#define BNX2_MISC_ENABLE_SET_BITS_RX_DMA_ENABLE                 (1L<<17)
+#define BNX2_MISC_ENABLE_SET_BITS_COMPLETION_ENABLE     (1L<<18)
+#define BNX2_MISC_ENABLE_SET_BITS_HOST_COALESCE_ENABLE  (1L<<19)
+#define BNX2_MISC_ENABLE_SET_BITS_MAILBOX_QUEUE_ENABLE  (1L<<20)
+#define BNX2_MISC_ENABLE_SET_BITS_CONTEXT_ENABLE        (1L<<21)
+#define BNX2_MISC_ENABLE_SET_BITS_CMD_SCHEDULER_ENABLE  (1L<<22)
+#define BNX2_MISC_ENABLE_SET_BITS_CMD_PROCESSOR_ENABLE  (1L<<23)
+#define BNX2_MISC_ENABLE_SET_BITS_MGMT_PROCESSOR_ENABLE         (1L<<24)
+#define BNX2_MISC_ENABLE_SET_BITS_TIMER_ENABLE          (1L<<25)
+#define BNX2_MISC_ENABLE_SET_BITS_DMA_ENGINE_ENABLE     (1L<<26)
+#define BNX2_MISC_ENABLE_SET_BITS_UMP_ENABLE            (1L<<27)
+
+#define BNX2_MISC_ENABLE_CLR_BITS                      0x00000814
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_SCHEDULER_ENABLE   (1L<<0)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_BD_READ_ENABLE     (1L<<1)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_BD_CACHE_ENABLE    (1L<<2)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_PROCESSOR_ENABLE   (1L<<3)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_DMA_ENABLE                 (1L<<4)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_PATCHUP_ENABLE     (1L<<5)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_PAYLOAD_Q_ENABLE   (1L<<6)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_HEADER_Q_ENABLE    (1L<<7)
+#define BNX2_MISC_ENABLE_CLR_BITS_TX_ASSEMBLER_ENABLE   (1L<<8)
+#define BNX2_MISC_ENABLE_CLR_BITS_EMAC_ENABLE           (1L<<9)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_PARSER_MAC_ENABLE  (1L<<10)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_PARSER_CATCHUP_ENABLE      (1L<<11)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_MBUF_ENABLE        (1L<<12)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_LOOKUP_ENABLE      (1L<<13)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_PROCESSOR_ENABLE   (1L<<14)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_V2P_ENABLE                 (1L<<15)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_BD_CACHE_ENABLE    (1L<<16)
+#define BNX2_MISC_ENABLE_CLR_BITS_RX_DMA_ENABLE                 (1L<<17)
+#define BNX2_MISC_ENABLE_CLR_BITS_COMPLETION_ENABLE     (1L<<18)
+#define BNX2_MISC_ENABLE_CLR_BITS_HOST_COALESCE_ENABLE  (1L<<19)
+#define BNX2_MISC_ENABLE_CLR_BITS_MAILBOX_QUEUE_ENABLE  (1L<<20)
+#define BNX2_MISC_ENABLE_CLR_BITS_CONTEXT_ENABLE        (1L<<21)
+#define BNX2_MISC_ENABLE_CLR_BITS_CMD_SCHEDULER_ENABLE  (1L<<22)
+#define BNX2_MISC_ENABLE_CLR_BITS_CMD_PROCESSOR_ENABLE  (1L<<23)
+#define BNX2_MISC_ENABLE_CLR_BITS_MGMT_PROCESSOR_ENABLE         (1L<<24)
+#define BNX2_MISC_ENABLE_CLR_BITS_TIMER_ENABLE          (1L<<25)
+#define BNX2_MISC_ENABLE_CLR_BITS_DMA_ENGINE_ENABLE     (1L<<26)
+#define BNX2_MISC_ENABLE_CLR_BITS_UMP_ENABLE            (1L<<27)
+
+#define BNX2_MISC_CLOCK_CONTROL_BITS                   0x00000818
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET    (0xfL<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_32MHZ      (0L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_38MHZ      (1L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_48MHZ      (2L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_55MHZ      (3L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_66MHZ      (4L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_80MHZ      (5L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_95MHZ      (6L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_133MHZ     (7L<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_LOW        (0xfL<<0)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_DISABLE   (1L<<6)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT       (1L<<7)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC   (0x7L<<8)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_UNDEF     (0L<<8)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_12        (1L<<8)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_6         (2L<<8)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_62        (4L<<8)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PLAY_DEAD          (1L<<11)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED         (0xfL<<12)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_100     (0L<<12)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_80      (1L<<12)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_50      (2L<<12)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_40      (4L<<12)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_25      (8L<<12)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_STOP  (1L<<16)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_PLL_STOP       (1L<<17)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_RESERVED_18        (1L<<18)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_USE_SPD_DET        (1L<<19)
+#define BNX2_MISC_CLOCK_CONTROL_BITS_RESERVED           (0xfffL<<20)
+
+#define BNX2_MISC_GPIO                                 0x0000081c
+#define BNX2_MISC_GPIO_VALUE                            (0xffL<<0)
+#define BNX2_MISC_GPIO_SET                              (0xffL<<8)
+#define BNX2_MISC_GPIO_CLR                              (0xffL<<16)
+#define BNX2_MISC_GPIO_FLOAT                            (0xffL<<24)
+
+#define BNX2_MISC_GPIO_INT                             0x00000820
+#define BNX2_MISC_GPIO_INT_INT_STATE                    (0xfL<<0)
+#define BNX2_MISC_GPIO_INT_OLD_VALUE                    (0xfL<<8)
+#define BNX2_MISC_GPIO_INT_OLD_SET                      (0xfL<<16)
+#define BNX2_MISC_GPIO_INT_OLD_CLR                      (0xfL<<24)
+
+#define BNX2_MISC_CONFIG_LFSR                          0x00000824
+#define BNX2_MISC_CONFIG_LFSR_DIV                       (0xffffL<<0)
+
+#define BNX2_MISC_LFSR_MASK_BITS                       0x00000828
+#define BNX2_MISC_LFSR_MASK_BITS_TX_SCHEDULER_ENABLE    (1L<<0)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_BD_READ_ENABLE      (1L<<1)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_BD_CACHE_ENABLE     (1L<<2)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_PROCESSOR_ENABLE    (1L<<3)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_DMA_ENABLE          (1L<<4)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_PATCHUP_ENABLE      (1L<<5)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_PAYLOAD_Q_ENABLE    (1L<<6)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_HEADER_Q_ENABLE     (1L<<7)
+#define BNX2_MISC_LFSR_MASK_BITS_TX_ASSEMBLER_ENABLE    (1L<<8)
+#define BNX2_MISC_LFSR_MASK_BITS_EMAC_ENABLE            (1L<<9)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_PARSER_MAC_ENABLE   (1L<<10)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_PARSER_CATCHUP_ENABLE       (1L<<11)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_MBUF_ENABLE                 (1L<<12)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_LOOKUP_ENABLE       (1L<<13)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_PROCESSOR_ENABLE    (1L<<14)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_V2P_ENABLE          (1L<<15)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_BD_CACHE_ENABLE     (1L<<16)
+#define BNX2_MISC_LFSR_MASK_BITS_RX_DMA_ENABLE          (1L<<17)
+#define BNX2_MISC_LFSR_MASK_BITS_COMPLETION_ENABLE      (1L<<18)
+#define BNX2_MISC_LFSR_MASK_BITS_HOST_COALESCE_ENABLE   (1L<<19)
+#define BNX2_MISC_LFSR_MASK_BITS_MAILBOX_QUEUE_ENABLE   (1L<<20)
+#define BNX2_MISC_LFSR_MASK_BITS_CONTEXT_ENABLE                 (1L<<21)
+#define BNX2_MISC_LFSR_MASK_BITS_CMD_SCHEDULER_ENABLE   (1L<<22)
+#define BNX2_MISC_LFSR_MASK_BITS_CMD_PROCESSOR_ENABLE   (1L<<23)
+#define BNX2_MISC_LFSR_MASK_BITS_MGMT_PROCESSOR_ENABLE  (1L<<24)
+#define BNX2_MISC_LFSR_MASK_BITS_TIMER_ENABLE           (1L<<25)
+#define BNX2_MISC_LFSR_MASK_BITS_DMA_ENGINE_ENABLE      (1L<<26)
+#define BNX2_MISC_LFSR_MASK_BITS_UMP_ENABLE             (1L<<27)
+
+#define BNX2_MISC_ARB_REQ0                             0x0000082c
+#define BNX2_MISC_ARB_REQ1                             0x00000830
+#define BNX2_MISC_ARB_REQ2                             0x00000834
+#define BNX2_MISC_ARB_REQ3                             0x00000838
+#define BNX2_MISC_ARB_REQ4                             0x0000083c
+#define BNX2_MISC_ARB_FREE0                            0x00000840
+#define BNX2_MISC_ARB_FREE1                            0x00000844
+#define BNX2_MISC_ARB_FREE2                            0x00000848
+#define BNX2_MISC_ARB_FREE3                            0x0000084c
+#define BNX2_MISC_ARB_FREE4                            0x00000850
+#define BNX2_MISC_ARB_REQ_STATUS0                      0x00000854
+#define BNX2_MISC_ARB_REQ_STATUS1                      0x00000858
+#define BNX2_MISC_ARB_REQ_STATUS2                      0x0000085c
+#define BNX2_MISC_ARB_REQ_STATUS3                      0x00000860
+#define BNX2_MISC_ARB_REQ_STATUS4                      0x00000864
+#define BNX2_MISC_ARB_GNT0                             0x00000868
+#define BNX2_MISC_ARB_GNT0_0                            (0x7L<<0)
+#define BNX2_MISC_ARB_GNT0_1                            (0x7L<<4)
+#define BNX2_MISC_ARB_GNT0_2                            (0x7L<<8)
+#define BNX2_MISC_ARB_GNT0_3                            (0x7L<<12)
+#define BNX2_MISC_ARB_GNT0_4                            (0x7L<<16)
+#define BNX2_MISC_ARB_GNT0_5                            (0x7L<<20)
+#define BNX2_MISC_ARB_GNT0_6                            (0x7L<<24)
+#define BNX2_MISC_ARB_GNT0_7                            (0x7L<<28)
+
+#define BNX2_MISC_ARB_GNT1                             0x0000086c
+#define BNX2_MISC_ARB_GNT1_8                            (0x7L<<0)
+#define BNX2_MISC_ARB_GNT1_9                            (0x7L<<4)
+#define BNX2_MISC_ARB_GNT1_10                           (0x7L<<8)
+#define BNX2_MISC_ARB_GNT1_11                           (0x7L<<12)
+#define BNX2_MISC_ARB_GNT1_12                           (0x7L<<16)
+#define BNX2_MISC_ARB_GNT1_13                           (0x7L<<20)
+#define BNX2_MISC_ARB_GNT1_14                           (0x7L<<24)
+#define BNX2_MISC_ARB_GNT1_15                           (0x7L<<28)
+
+#define BNX2_MISC_ARB_GNT2                             0x00000870
+#define BNX2_MISC_ARB_GNT2_16                           (0x7L<<0)
+#define BNX2_MISC_ARB_GNT2_17                           (0x7L<<4)
+#define BNX2_MISC_ARB_GNT2_18                           (0x7L<<8)
+#define BNX2_MISC_ARB_GNT2_19                           (0x7L<<12)
+#define BNX2_MISC_ARB_GNT2_20                           (0x7L<<16)
+#define BNX2_MISC_ARB_GNT2_21                           (0x7L<<20)
+#define BNX2_MISC_ARB_GNT2_22                           (0x7L<<24)
+#define BNX2_MISC_ARB_GNT2_23                           (0x7L<<28)
+
+#define BNX2_MISC_ARB_GNT3                             0x00000874
+#define BNX2_MISC_ARB_GNT3_24                           (0x7L<<0)
+#define BNX2_MISC_ARB_GNT3_25                           (0x7L<<4)
+#define BNX2_MISC_ARB_GNT3_26                           (0x7L<<8)
+#define BNX2_MISC_ARB_GNT3_27                           (0x7L<<12)
+#define BNX2_MISC_ARB_GNT3_28                           (0x7L<<16)
+#define BNX2_MISC_ARB_GNT3_29                           (0x7L<<20)
+#define BNX2_MISC_ARB_GNT3_30                           (0x7L<<24)
+#define BNX2_MISC_ARB_GNT3_31                           (0x7L<<28)
+
+#define BNX2_MISC_PRBS_CONTROL                         0x00000878
+#define BNX2_MISC_PRBS_CONTROL_EN                       (1L<<0)
+#define BNX2_MISC_PRBS_CONTROL_RSTB                     (1L<<1)
+#define BNX2_MISC_PRBS_CONTROL_INV                      (1L<<2)
+#define BNX2_MISC_PRBS_CONTROL_ERR_CLR                  (1L<<3)
+#define BNX2_MISC_PRBS_CONTROL_ORDER                    (0x3L<<4)
+#define BNX2_MISC_PRBS_CONTROL_ORDER_7TH                (0L<<4)
+#define BNX2_MISC_PRBS_CONTROL_ORDER_15TH               (1L<<4)
+#define BNX2_MISC_PRBS_CONTROL_ORDER_23RD               (2L<<4)
+#define BNX2_MISC_PRBS_CONTROL_ORDER_31ST               (3L<<4)
+
+#define BNX2_MISC_PRBS_STATUS                          0x0000087c
+#define BNX2_MISC_PRBS_STATUS_LOCK                      (1L<<0)
+#define BNX2_MISC_PRBS_STATUS_STKY                      (1L<<1)
+#define BNX2_MISC_PRBS_STATUS_ERRORS                    (0x3fffL<<2)
+#define BNX2_MISC_PRBS_STATUS_STATE                     (0xfL<<16)
+
+#define BNX2_MISC_SM_ASF_CONTROL                       0x00000880
+#define BNX2_MISC_SM_ASF_CONTROL_ASF_RST                (1L<<0)
+#define BNX2_MISC_SM_ASF_CONTROL_TSC_EN                         (1L<<1)
+#define BNX2_MISC_SM_ASF_CONTROL_WG_TO                  (1L<<2)
+#define BNX2_MISC_SM_ASF_CONTROL_HB_TO                  (1L<<3)
+#define BNX2_MISC_SM_ASF_CONTROL_PA_TO                  (1L<<4)
+#define BNX2_MISC_SM_ASF_CONTROL_PL_TO                  (1L<<5)
+#define BNX2_MISC_SM_ASF_CONTROL_RT_TO                  (1L<<6)
+#define BNX2_MISC_SM_ASF_CONTROL_SMB_EVENT              (1L<<7)
+#define BNX2_MISC_SM_ASF_CONTROL_RES                    (0xfL<<8)
+#define BNX2_MISC_SM_ASF_CONTROL_SMB_EN                         (1L<<12)
+#define BNX2_MISC_SM_ASF_CONTROL_SMB_BB_EN              (1L<<13)
+#define BNX2_MISC_SM_ASF_CONTROL_SMB_NO_ADDR_FILT       (1L<<14)
+#define BNX2_MISC_SM_ASF_CONTROL_SMB_AUTOREAD           (1L<<15)
+#define BNX2_MISC_SM_ASF_CONTROL_NIC_SMB_ADDR1          (0x3fL<<16)
+#define BNX2_MISC_SM_ASF_CONTROL_NIC_SMB_ADDR2          (0x3fL<<24)
+#define BNX2_MISC_SM_ASF_CONTROL_EN_NIC_SMB_ADDR_0      (1L<<30)
+#define BNX2_MISC_SM_ASF_CONTROL_SMB_EARLY_ATTN                 (1L<<31)
+
+#define BNX2_MISC_SMB_IN                               0x00000884
+#define BNX2_MISC_SMB_IN_DAT_IN                                 (0xffL<<0)
+#define BNX2_MISC_SMB_IN_RDY                            (1L<<8)
+#define BNX2_MISC_SMB_IN_DONE                           (1L<<9)
+#define BNX2_MISC_SMB_IN_FIRSTBYTE                      (1L<<10)
+#define BNX2_MISC_SMB_IN_STATUS                                 (0x7L<<11)
+#define BNX2_MISC_SMB_IN_STATUS_OK                      (0x0L<<11)
+#define BNX2_MISC_SMB_IN_STATUS_PEC                     (0x1L<<11)
+#define BNX2_MISC_SMB_IN_STATUS_OFLOW                   (0x2L<<11)
+#define BNX2_MISC_SMB_IN_STATUS_STOP                    (0x3L<<11)
+#define BNX2_MISC_SMB_IN_STATUS_TIMEOUT                         (0x4L<<11)
+
+#define BNX2_MISC_SMB_OUT                              0x00000888
+#define BNX2_MISC_SMB_OUT_DAT_OUT                       (0xffL<<0)
+#define BNX2_MISC_SMB_OUT_RDY                           (1L<<8)
+#define BNX2_MISC_SMB_OUT_START                                 (1L<<9)
+#define BNX2_MISC_SMB_OUT_LAST                          (1L<<10)
+#define BNX2_MISC_SMB_OUT_ACC_TYPE                      (1L<<11)
+#define BNX2_MISC_SMB_OUT_ENB_PEC                       (1L<<12)
+#define BNX2_MISC_SMB_OUT_GET_RX_LEN                    (1L<<13)
+#define BNX2_MISC_SMB_OUT_SMB_READ_LEN                  (0x3fL<<14)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS                (0xfL<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_OK             (0L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_FIRST_NACK     (1L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_SUB_NACK       (9L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_UFLOW          (2L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_STOP           (3L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_TIMEOUT        (4L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_FIRST_LOST     (5L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_SUB_LOST       (0xdL<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_BADACK                 (0x6L<<20)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_SLAVEMODE             (1L<<24)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_DAT_EN                (1L<<25)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_DAT_IN                (1L<<26)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_CLK_EN                (1L<<27)
+#define BNX2_MISC_SMB_OUT_SMB_OUT_CLK_IN                (1L<<28)
+
+#define BNX2_MISC_SMB_WATCHDOG                         0x0000088c
+#define BNX2_MISC_SMB_WATCHDOG_WATCHDOG                         (0xffffL<<0)
+
+#define BNX2_MISC_SMB_HEARTBEAT                                0x00000890
+#define BNX2_MISC_SMB_HEARTBEAT_HEARTBEAT               (0xffffL<<0)
+
+#define BNX2_MISC_SMB_POLL_ASF                         0x00000894
+#define BNX2_MISC_SMB_POLL_ASF_POLL_ASF                         (0xffffL<<0)
+
+#define BNX2_MISC_SMB_POLL_LEGACY                      0x00000898
+#define BNX2_MISC_SMB_POLL_LEGACY_POLL_LEGACY           (0xffffL<<0)
+
+#define BNX2_MISC_SMB_RETRAN                           0x0000089c
+#define BNX2_MISC_SMB_RETRAN_RETRAN                     (0xffL<<0)
+
+#define BNX2_MISC_SMB_TIMESTAMP                                0x000008a0
+#define BNX2_MISC_SMB_TIMESTAMP_TIMESTAMP               (0xffffffffL<<0)
+
+#define BNX2_MISC_PERR_ENA0                            0x000008a4
+#define BNX2_MISC_PERR_ENA0_COM_MISC_CTXC               (1L<<0)
+#define BNX2_MISC_PERR_ENA0_COM_MISC_REGF               (1L<<1)
+#define BNX2_MISC_PERR_ENA0_COM_MISC_SCPAD              (1L<<2)
+#define BNX2_MISC_PERR_ENA0_CP_MISC_CTXC                (1L<<3)
+#define BNX2_MISC_PERR_ENA0_CP_MISC_REGF                (1L<<4)
+#define BNX2_MISC_PERR_ENA0_CP_MISC_SCPAD               (1L<<5)
+#define BNX2_MISC_PERR_ENA0_CS_MISC_TMEM                (1L<<6)
+#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM0              (1L<<7)
+#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM1              (1L<<8)
+#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM2              (1L<<9)
+#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM3              (1L<<10)
+#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM4              (1L<<11)
+#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM5              (1L<<12)
+#define BNX2_MISC_PERR_ENA0_CTX_MISC_PGTBL              (1L<<13)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR0               (1L<<14)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR1               (1L<<15)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR2               (1L<<16)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR3               (1L<<17)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR4               (1L<<18)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DW0               (1L<<19)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DW1               (1L<<20)
+#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DW2               (1L<<21)
+#define BNX2_MISC_PERR_ENA0_HC_MISC_DMA                         (1L<<22)
+#define BNX2_MISC_PERR_ENA0_MCP_MISC_REGF               (1L<<23)
+#define BNX2_MISC_PERR_ENA0_MCP_MISC_SCPAD              (1L<<24)
+#define BNX2_MISC_PERR_ENA0_MQ_MISC_CTX                         (1L<<25)
+#define BNX2_MISC_PERR_ENA0_RBDC_MISC                   (1L<<26)
+#define BNX2_MISC_PERR_ENA0_RBUF_MISC_MB                (1L<<27)
+#define BNX2_MISC_PERR_ENA0_RBUF_MISC_PTR               (1L<<28)
+#define BNX2_MISC_PERR_ENA0_RDE_MISC_RPC                (1L<<29)
+#define BNX2_MISC_PERR_ENA0_RDE_MISC_RPM                (1L<<30)
+#define BNX2_MISC_PERR_ENA0_RV2P_MISC_CB0REGS           (1L<<31)
+
+#define BNX2_MISC_PERR_ENA1                            0x000008a8
+#define BNX2_MISC_PERR_ENA1_RV2P_MISC_CB1REGS           (1L<<0)
+#define BNX2_MISC_PERR_ENA1_RV2P_MISC_P1IRAM            (1L<<1)
+#define BNX2_MISC_PERR_ENA1_RV2P_MISC_P2IRAM            (1L<<2)
+#define BNX2_MISC_PERR_ENA1_RXP_MISC_CTXC               (1L<<3)
+#define BNX2_MISC_PERR_ENA1_RXP_MISC_REGF               (1L<<4)
+#define BNX2_MISC_PERR_ENA1_RXP_MISC_SCPAD              (1L<<5)
+#define BNX2_MISC_PERR_ENA1_RXP_MISC_RBUFC              (1L<<6)
+#define BNX2_MISC_PERR_ENA1_TBDC_MISC                   (1L<<7)
+#define BNX2_MISC_PERR_ENA1_TDMA_MISC                   (1L<<8)
+#define BNX2_MISC_PERR_ENA1_THBUF_MISC_MB0              (1L<<9)
+#define BNX2_MISC_PERR_ENA1_THBUF_MISC_MB1              (1L<<10)
+#define BNX2_MISC_PERR_ENA1_TPAT_MISC_REGF              (1L<<11)
+#define BNX2_MISC_PERR_ENA1_TPAT_MISC_SCPAD             (1L<<12)
+#define BNX2_MISC_PERR_ENA1_TPBUF_MISC_MB               (1L<<13)
+#define BNX2_MISC_PERR_ENA1_TSCH_MISC_LR                (1L<<14)
+#define BNX2_MISC_PERR_ENA1_TXP_MISC_CTXC               (1L<<15)
+#define BNX2_MISC_PERR_ENA1_TXP_MISC_REGF               (1L<<16)
+#define BNX2_MISC_PERR_ENA1_TXP_MISC_SCPAD              (1L<<17)
+#define BNX2_MISC_PERR_ENA1_UMP_MISC_FIORX              (1L<<18)
+#define BNX2_MISC_PERR_ENA1_UMP_MISC_FIOTX              (1L<<19)
+#define BNX2_MISC_PERR_ENA1_UMP_MISC_RX                         (1L<<20)
+#define BNX2_MISC_PERR_ENA1_UMP_MISC_TX                         (1L<<21)
+#define BNX2_MISC_PERR_ENA1_RDMAQ_MISC                  (1L<<22)
+#define BNX2_MISC_PERR_ENA1_CSQ_MISC                    (1L<<23)
+#define BNX2_MISC_PERR_ENA1_CPQ_MISC                    (1L<<24)
+#define BNX2_MISC_PERR_ENA1_MCPQ_MISC                   (1L<<25)
+#define BNX2_MISC_PERR_ENA1_RV2PMQ_MISC                         (1L<<26)
+#define BNX2_MISC_PERR_ENA1_RV2PPQ_MISC                         (1L<<27)
+#define BNX2_MISC_PERR_ENA1_RV2PTQ_MISC                         (1L<<28)
+#define BNX2_MISC_PERR_ENA1_RXPQ_MISC                   (1L<<29)
+#define BNX2_MISC_PERR_ENA1_RXPCQ_MISC                  (1L<<30)
+#define BNX2_MISC_PERR_ENA1_RLUPQ_MISC                  (1L<<31)
+
+#define BNX2_MISC_PERR_ENA2                            0x000008ac
+#define BNX2_MISC_PERR_ENA2_COMQ_MISC                   (1L<<0)
+#define BNX2_MISC_PERR_ENA2_COMXQ_MISC                  (1L<<1)
+#define BNX2_MISC_PERR_ENA2_COMTQ_MISC                  (1L<<2)
+#define BNX2_MISC_PERR_ENA2_TSCHQ_MISC                  (1L<<3)
+#define BNX2_MISC_PERR_ENA2_TBDRQ_MISC                  (1L<<4)
+#define BNX2_MISC_PERR_ENA2_TXPQ_MISC                   (1L<<5)
+#define BNX2_MISC_PERR_ENA2_TDMAQ_MISC                  (1L<<6)
+#define BNX2_MISC_PERR_ENA2_TPATQ_MISC                  (1L<<7)
+#define BNX2_MISC_PERR_ENA2_TASQ_MISC                   (1L<<8)
+
+#define BNX2_MISC_DEBUG_VECTOR_SEL                     0x000008b0
+#define BNX2_MISC_DEBUG_VECTOR_SEL_0                    (0xfffL<<0)
+#define BNX2_MISC_DEBUG_VECTOR_SEL_1                    (0xfffL<<12)
+
+#define BNX2_MISC_VREG_CONTROL                         0x000008b4
+#define BNX2_MISC_VREG_CONTROL_1_2                      (0xfL<<0)
+#define BNX2_MISC_VREG_CONTROL_2_5                      (0xfL<<4)
+
+#define BNX2_MISC_FINAL_CLK_CTL_VAL                    0x000008b8
+#define BNX2_MISC_FINAL_CLK_CTL_VAL_MISC_FINAL_CLK_CTL_VAL      (0x3ffffffL<<6)
+
+#define BNX2_MISC_UNUSED0                              0x000008bc
+
+
+/*
+ *  nvm_reg definition
+ *  offset: 0x6400
+ */
+#define BNX2_NVM_COMMAND                               0x00006400
+#define BNX2_NVM_COMMAND_RST                            (1L<<0)
+#define BNX2_NVM_COMMAND_DONE                           (1L<<3)
+#define BNX2_NVM_COMMAND_DOIT                           (1L<<4)
+#define BNX2_NVM_COMMAND_WR                             (1L<<5)
+#define BNX2_NVM_COMMAND_ERASE                          (1L<<6)
+#define BNX2_NVM_COMMAND_FIRST                          (1L<<7)
+#define BNX2_NVM_COMMAND_LAST                           (1L<<8)
+#define BNX2_NVM_COMMAND_WREN                           (1L<<16)
+#define BNX2_NVM_COMMAND_WRDI                           (1L<<17)
+#define BNX2_NVM_COMMAND_EWSR                           (1L<<18)
+#define BNX2_NVM_COMMAND_WRSR                           (1L<<19)
+
+#define BNX2_NVM_STATUS                                        0x00006404
+#define BNX2_NVM_STATUS_PI_FSM_STATE                    (0xfL<<0)
+#define BNX2_NVM_STATUS_EE_FSM_STATE                    (0xfL<<4)
+#define BNX2_NVM_STATUS_EQ_FSM_STATE                    (0xfL<<8)
+
+#define BNX2_NVM_WRITE                                 0x00006408
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE                  (0xffffffffL<<0)
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_BIT_BANG                 (0L<<0)
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_EECLK            (1L<<0)
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_EEDATA           (2L<<0)
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_SCLK             (4L<<0)
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_CS_B             (8L<<0)
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_SO               (16L<<0)
+#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_SI               (32L<<0)
+
+#define BNX2_NVM_ADDR                                  0x0000640c
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE                    (0xffffffL<<0)
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_BIT_BANG           (0L<<0)
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_EECLK              (1L<<0)
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_EEDATA             (2L<<0)
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_SCLK               (4L<<0)
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_CS_B               (8L<<0)
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_SO                         (16L<<0)
+#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_SI                         (32L<<0)
+
+#define BNX2_NVM_READ                                  0x00006410
+#define BNX2_NVM_READ_NVM_READ_VALUE                    (0xffffffffL<<0)
+#define BNX2_NVM_READ_NVM_READ_VALUE_BIT_BANG           (0L<<0)
+#define BNX2_NVM_READ_NVM_READ_VALUE_EECLK              (1L<<0)
+#define BNX2_NVM_READ_NVM_READ_VALUE_EEDATA             (2L<<0)
+#define BNX2_NVM_READ_NVM_READ_VALUE_SCLK               (4L<<0)
+#define BNX2_NVM_READ_NVM_READ_VALUE_CS_B               (8L<<0)
+#define BNX2_NVM_READ_NVM_READ_VALUE_SO                         (16L<<0)
+#define BNX2_NVM_READ_NVM_READ_VALUE_SI                         (32L<<0)
+
+#define BNX2_NVM_CFG1                                  0x00006414
+#define BNX2_NVM_CFG1_FLASH_MODE                        (1L<<0)
+#define BNX2_NVM_CFG1_BUFFER_MODE                       (1L<<1)
+#define BNX2_NVM_CFG1_PASS_MODE                                 (1L<<2)
+#define BNX2_NVM_CFG1_BITBANG_MODE                      (1L<<3)
+#define BNX2_NVM_CFG1_STATUS_BIT                        (0x7L<<4)
+#define BNX2_NVM_CFG1_STATUS_BIT_FLASH_RDY              (0L<<4)
+#define BNX2_NVM_CFG1_STATUS_BIT_BUFFER_RDY             (7L<<4)
+#define BNX2_NVM_CFG1_SPI_CLK_DIV                       (0xfL<<7)
+#define BNX2_NVM_CFG1_SEE_CLK_DIV                       (0x7ffL<<11)
+#define BNX2_NVM_CFG1_PROTECT_MODE                      (1L<<24)
+#define BNX2_NVM_CFG1_FLASH_SIZE                        (1L<<25)
+#define BNX2_NVM_CFG1_COMPAT_BYPASSS                    (1L<<31)
+
+#define BNX2_NVM_CFG2                                  0x00006418
+#define BNX2_NVM_CFG2_ERASE_CMD                                 (0xffL<<0)
+#define BNX2_NVM_CFG2_DUMMY                             (0xffL<<8)
+#define BNX2_NVM_CFG2_STATUS_CMD                        (0xffL<<16)
+
+#define BNX2_NVM_CFG3                                  0x0000641c
+#define BNX2_NVM_CFG3_BUFFER_RD_CMD                     (0xffL<<0)
+#define BNX2_NVM_CFG3_WRITE_CMD                                 (0xffL<<8)
+#define BNX2_NVM_CFG3_BUFFER_WRITE_CMD                  (0xffL<<16)
+#define BNX2_NVM_CFG3_READ_CMD                          (0xffL<<24)
+
+#define BNX2_NVM_SW_ARB                                        0x00006420
+#define BNX2_NVM_SW_ARB_ARB_REQ_SET0                    (1L<<0)
+#define BNX2_NVM_SW_ARB_ARB_REQ_SET1                    (1L<<1)
+#define BNX2_NVM_SW_ARB_ARB_REQ_SET2                    (1L<<2)
+#define BNX2_NVM_SW_ARB_ARB_REQ_SET3                    (1L<<3)
+#define BNX2_NVM_SW_ARB_ARB_REQ_CLR0                    (1L<<4)
+#define BNX2_NVM_SW_ARB_ARB_REQ_CLR1                    (1L<<5)
+#define BNX2_NVM_SW_ARB_ARB_REQ_CLR2                    (1L<<6)
+#define BNX2_NVM_SW_ARB_ARB_REQ_CLR3                    (1L<<7)
+#define BNX2_NVM_SW_ARB_ARB_ARB0                        (1L<<8)
+#define BNX2_NVM_SW_ARB_ARB_ARB1                        (1L<<9)
+#define BNX2_NVM_SW_ARB_ARB_ARB2                        (1L<<10)
+#define BNX2_NVM_SW_ARB_ARB_ARB3                        (1L<<11)
+#define BNX2_NVM_SW_ARB_REQ0                            (1L<<12)
+#define BNX2_NVM_SW_ARB_REQ1                            (1L<<13)
+#define BNX2_NVM_SW_ARB_REQ2                            (1L<<14)
+#define BNX2_NVM_SW_ARB_REQ3                            (1L<<15)
+
+#define BNX2_NVM_ACCESS_ENABLE                         0x00006424
+#define BNX2_NVM_ACCESS_ENABLE_EN                       (1L<<0)
+#define BNX2_NVM_ACCESS_ENABLE_WR_EN                    (1L<<1)
+
+#define BNX2_NVM_WRITE1                                        0x00006428
+#define BNX2_NVM_WRITE1_WREN_CMD                        (0xffL<<0)
+#define BNX2_NVM_WRITE1_WRDI_CMD                        (0xffL<<8)
+#define BNX2_NVM_WRITE1_SR_DATA                                 (0xffL<<16)
+
+
+
+/*
+ *  dma_reg definition
+ *  offset: 0xc00
+ */
+#define BNX2_DMA_COMMAND                               0x00000c00
+#define BNX2_DMA_COMMAND_ENABLE                                 (1L<<0)
+
+#define BNX2_DMA_STATUS                                        0x00000c04
+#define BNX2_DMA_STATUS_PAR_ERROR_STATE                         (1L<<0)
+#define BNX2_DMA_STATUS_READ_TRANSFERS_STAT             (1L<<16)
+#define BNX2_DMA_STATUS_READ_DELAY_PCI_CLKS_STAT        (1L<<17)
+#define BNX2_DMA_STATUS_BIG_READ_TRANSFERS_STAT                 (1L<<18)
+#define BNX2_DMA_STATUS_BIG_READ_DELAY_PCI_CLKS_STAT    (1L<<19)
+#define BNX2_DMA_STATUS_BIG_READ_RETRY_AFTER_DATA_STAT  (1L<<20)
+#define BNX2_DMA_STATUS_WRITE_TRANSFERS_STAT            (1L<<21)
+#define BNX2_DMA_STATUS_WRITE_DELAY_PCI_CLKS_STAT       (1L<<22)
+#define BNX2_DMA_STATUS_BIG_WRITE_TRANSFERS_STAT        (1L<<23)
+#define BNX2_DMA_STATUS_BIG_WRITE_DELAY_PCI_CLKS_STAT   (1L<<24)
+#define BNX2_DMA_STATUS_BIG_WRITE_RETRY_AFTER_DATA_STAT         (1L<<25)
+
+#define BNX2_DMA_CONFIG                                        0x00000c08
+#define BNX2_DMA_CONFIG_DATA_BYTE_SWAP                  (1L<<0)
+#define BNX2_DMA_CONFIG_DATA_WORD_SWAP                  (1L<<1)
+#define BNX2_DMA_CONFIG_CNTL_BYTE_SWAP                  (1L<<4)
+#define BNX2_DMA_CONFIG_CNTL_WORD_SWAP                  (1L<<5)
+#define BNX2_DMA_CONFIG_ONE_DMA                                 (1L<<6)
+#define BNX2_DMA_CONFIG_CNTL_TWO_DMA                    (1L<<7)
+#define BNX2_DMA_CONFIG_CNTL_FPGA_MODE                  (1L<<8)
+#define BNX2_DMA_CONFIG_CNTL_PING_PONG_DMA              (1L<<10)
+#define BNX2_DMA_CONFIG_CNTL_PCI_COMP_DLY               (1L<<11)
+#define BNX2_DMA_CONFIG_NO_RCHANS_IN_USE                (0xfL<<12)
+#define BNX2_DMA_CONFIG_NO_WCHANS_IN_USE                (0xfL<<16)
+#define BNX2_DMA_CONFIG_PCI_CLK_CMP_BITS                (0x7L<<20)
+#define BNX2_DMA_CONFIG_PCI_FAST_CLK_CMP                (1L<<23)
+#define BNX2_DMA_CONFIG_BIG_SIZE                        (0xfL<<24)
+#define BNX2_DMA_CONFIG_BIG_SIZE_NONE                   (0x0L<<24)
+#define BNX2_DMA_CONFIG_BIG_SIZE_64                     (0x1L<<24)
+#define BNX2_DMA_CONFIG_BIG_SIZE_128                    (0x2L<<24)
+#define BNX2_DMA_CONFIG_BIG_SIZE_256                    (0x4L<<24)
+#define BNX2_DMA_CONFIG_BIG_SIZE_512                    (0x8L<<24)
+
+#define BNX2_DMA_BLACKOUT                              0x00000c0c
+#define BNX2_DMA_BLACKOUT_RD_RETRY_BLACKOUT             (0xffL<<0)
+#define BNX2_DMA_BLACKOUT_2ND_RD_RETRY_BLACKOUT                 (0xffL<<8)
+#define BNX2_DMA_BLACKOUT_WR_RETRY_BLACKOUT             (0xffL<<16)
+
+#define BNX2_DMA_RCHAN_STAT                            0x00000c30
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_0                         (0x7L<<0)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_0                   (1L<<3)
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_1                         (0x7L<<4)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_1                   (1L<<7)
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_2                         (0x7L<<8)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_2                   (1L<<11)
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_3                         (0x7L<<12)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_3                   (1L<<15)
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_4                         (0x7L<<16)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_4                   (1L<<19)
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_5                         (0x7L<<20)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_5                   (1L<<23)
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_6                         (0x7L<<24)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_6                   (1L<<27)
+#define BNX2_DMA_RCHAN_STAT_COMP_CODE_7                         (0x7L<<28)
+#define BNX2_DMA_RCHAN_STAT_PAR_ERR_7                   (1L<<31)
+
+#define BNX2_DMA_WCHAN_STAT                            0x00000c34
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_0                         (0x7L<<0)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_0                   (1L<<3)
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_1                         (0x7L<<4)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_1                   (1L<<7)
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_2                         (0x7L<<8)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_2                   (1L<<11)
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_3                         (0x7L<<12)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_3                   (1L<<15)
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_4                         (0x7L<<16)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_4                   (1L<<19)
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_5                         (0x7L<<20)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_5                   (1L<<23)
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_6                         (0x7L<<24)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_6                   (1L<<27)
+#define BNX2_DMA_WCHAN_STAT_COMP_CODE_7                         (0x7L<<28)
+#define BNX2_DMA_WCHAN_STAT_PAR_ERR_7                   (1L<<31)
+
+#define BNX2_DMA_RCHAN_ASSIGNMENT                      0x00000c38
+#define BNX2_DMA_RCHAN_ASSIGNMENT_0                     (0xfL<<0)
+#define BNX2_DMA_RCHAN_ASSIGNMENT_1                     (0xfL<<4)
+#define BNX2_DMA_RCHAN_ASSIGNMENT_2                     (0xfL<<8)
+#define BNX2_DMA_RCHAN_ASSIGNMENT_3                     (0xfL<<12)
+#define BNX2_DMA_RCHAN_ASSIGNMENT_4                     (0xfL<<16)
+#define BNX2_DMA_RCHAN_ASSIGNMENT_5                     (0xfL<<20)
+#define BNX2_DMA_RCHAN_ASSIGNMENT_6                     (0xfL<<24)
+#define BNX2_DMA_RCHAN_ASSIGNMENT_7                     (0xfL<<28)
+
+#define BNX2_DMA_WCHAN_ASSIGNMENT                      0x00000c3c
+#define BNX2_DMA_WCHAN_ASSIGNMENT_0                     (0xfL<<0)
+#define BNX2_DMA_WCHAN_ASSIGNMENT_1                     (0xfL<<4)
+#define BNX2_DMA_WCHAN_ASSIGNMENT_2                     (0xfL<<8)
+#define BNX2_DMA_WCHAN_ASSIGNMENT_3                     (0xfL<<12)
+#define BNX2_DMA_WCHAN_ASSIGNMENT_4                     (0xfL<<16)
+#define BNX2_DMA_WCHAN_ASSIGNMENT_5                     (0xfL<<20)
+#define BNX2_DMA_WCHAN_ASSIGNMENT_6                     (0xfL<<24)
+#define BNX2_DMA_WCHAN_ASSIGNMENT_7                     (0xfL<<28)
+
+#define BNX2_DMA_RCHAN_STAT_00                         0x00000c40
+#define BNX2_DMA_RCHAN_STAT_00_RCHAN_STA_HOST_ADDR_LOW  (0xffffffffL<<0)
+
+#define BNX2_DMA_RCHAN_STAT_01                         0x00000c44
+#define BNX2_DMA_RCHAN_STAT_01_RCHAN_STA_HOST_ADDR_HIGH         (0xffffffffL<<0)
+
+#define BNX2_DMA_RCHAN_STAT_02                         0x00000c48
+#define BNX2_DMA_RCHAN_STAT_02_LENGTH                   (0xffffL<<0)
+#define BNX2_DMA_RCHAN_STAT_02_WORD_SWAP                (1L<<16)
+#define BNX2_DMA_RCHAN_STAT_02_BYTE_SWAP                (1L<<17)
+#define BNX2_DMA_RCHAN_STAT_02_PRIORITY_LVL             (1L<<18)
+
+#define BNX2_DMA_RCHAN_STAT_10                         0x00000c4c
+#define BNX2_DMA_RCHAN_STAT_11                         0x00000c50
+#define BNX2_DMA_RCHAN_STAT_12                         0x00000c54
+#define BNX2_DMA_RCHAN_STAT_20                         0x00000c58
+#define BNX2_DMA_RCHAN_STAT_21                         0x00000c5c
+#define BNX2_DMA_RCHAN_STAT_22                         0x00000c60
+#define BNX2_DMA_RCHAN_STAT_30                         0x00000c64
+#define BNX2_DMA_RCHAN_STAT_31                         0x00000c68
+#define BNX2_DMA_RCHAN_STAT_32                         0x00000c6c
+#define BNX2_DMA_RCHAN_STAT_40                         0x00000c70
+#define BNX2_DMA_RCHAN_STAT_41                         0x00000c74
+#define BNX2_DMA_RCHAN_STAT_42                         0x00000c78
+#define BNX2_DMA_RCHAN_STAT_50                         0x00000c7c
+#define BNX2_DMA_RCHAN_STAT_51                         0x00000c80
+#define BNX2_DMA_RCHAN_STAT_52                         0x00000c84
+#define BNX2_DMA_RCHAN_STAT_60                         0x00000c88
+#define BNX2_DMA_RCHAN_STAT_61                         0x00000c8c
+#define BNX2_DMA_RCHAN_STAT_62                         0x00000c90
+#define BNX2_DMA_RCHAN_STAT_70                         0x00000c94
+#define BNX2_DMA_RCHAN_STAT_71                         0x00000c98
+#define BNX2_DMA_RCHAN_STAT_72                         0x00000c9c
+#define BNX2_DMA_WCHAN_STAT_00                         0x00000ca0
+#define BNX2_DMA_WCHAN_STAT_00_WCHAN_STA_HOST_ADDR_LOW  (0xffffffffL<<0)
+
+#define BNX2_DMA_WCHAN_STAT_01                         0x00000ca4
+#define BNX2_DMA_WCHAN_STAT_01_WCHAN_STA_HOST_ADDR_HIGH         (0xffffffffL<<0)
+
+#define BNX2_DMA_WCHAN_STAT_02                         0x00000ca8
+#define BNX2_DMA_WCHAN_STAT_02_LENGTH                   (0xffffL<<0)
+#define BNX2_DMA_WCHAN_STAT_02_WORD_SWAP                (1L<<16)
+#define BNX2_DMA_WCHAN_STAT_02_BYTE_SWAP                (1L<<17)
+#define BNX2_DMA_WCHAN_STAT_02_PRIORITY_LVL             (1L<<18)
+
+#define BNX2_DMA_WCHAN_STAT_10                         0x00000cac
+#define BNX2_DMA_WCHAN_STAT_11                         0x00000cb0
+#define BNX2_DMA_WCHAN_STAT_12                         0x00000cb4
+#define BNX2_DMA_WCHAN_STAT_20                         0x00000cb8
+#define BNX2_DMA_WCHAN_STAT_21                         0x00000cbc
+#define BNX2_DMA_WCHAN_STAT_22                         0x00000cc0
+#define BNX2_DMA_WCHAN_STAT_30                         0x00000cc4
+#define BNX2_DMA_WCHAN_STAT_31                         0x00000cc8
+#define BNX2_DMA_WCHAN_STAT_32                         0x00000ccc
+#define BNX2_DMA_WCHAN_STAT_40                         0x00000cd0
+#define BNX2_DMA_WCHAN_STAT_41                         0x00000cd4
+#define BNX2_DMA_WCHAN_STAT_42                         0x00000cd8
+#define BNX2_DMA_WCHAN_STAT_50                         0x00000cdc
+#define BNX2_DMA_WCHAN_STAT_51                         0x00000ce0
+#define BNX2_DMA_WCHAN_STAT_52                         0x00000ce4
+#define BNX2_DMA_WCHAN_STAT_60                         0x00000ce8
+#define BNX2_DMA_WCHAN_STAT_61                         0x00000cec
+#define BNX2_DMA_WCHAN_STAT_62                         0x00000cf0
+#define BNX2_DMA_WCHAN_STAT_70                         0x00000cf4
+#define BNX2_DMA_WCHAN_STAT_71                         0x00000cf8
+#define BNX2_DMA_WCHAN_STAT_72                         0x00000cfc
+#define BNX2_DMA_ARB_STAT_00                           0x00000d00
+#define BNX2_DMA_ARB_STAT_00_MASTER                     (0xffffL<<0)
+#define BNX2_DMA_ARB_STAT_00_MASTER_ENC                         (0xffL<<16)
+#define BNX2_DMA_ARB_STAT_00_CUR_BINMSTR                (0xffL<<24)
+
+#define BNX2_DMA_ARB_STAT_01                           0x00000d04
+#define BNX2_DMA_ARB_STAT_01_LPR_RPTR                   (0xfL<<0)
+#define BNX2_DMA_ARB_STAT_01_LPR_WPTR                   (0xfL<<4)
+#define BNX2_DMA_ARB_STAT_01_LPB_RPTR                   (0xfL<<8)
+#define BNX2_DMA_ARB_STAT_01_LPB_WPTR                   (0xfL<<12)
+#define BNX2_DMA_ARB_STAT_01_HPR_RPTR                   (0xfL<<16)
+#define BNX2_DMA_ARB_STAT_01_HPR_WPTR                   (0xfL<<20)
+#define BNX2_DMA_ARB_STAT_01_HPB_RPTR                   (0xfL<<24)
+#define BNX2_DMA_ARB_STAT_01_HPB_WPTR                   (0xfL<<28)
+
+#define BNX2_DMA_FUSE_CTRL0_CMD                                0x00000f00
+#define BNX2_DMA_FUSE_CTRL0_CMD_PWRUP_DONE              (1L<<0)
+#define BNX2_DMA_FUSE_CTRL0_CMD_SHIFT_DONE              (1L<<1)
+#define BNX2_DMA_FUSE_CTRL0_CMD_SHIFT                   (1L<<2)
+#define BNX2_DMA_FUSE_CTRL0_CMD_LOAD                    (1L<<3)
+#define BNX2_DMA_FUSE_CTRL0_CMD_SEL                     (0xfL<<8)
+
+#define BNX2_DMA_FUSE_CTRL0_DATA                       0x00000f04
+#define BNX2_DMA_FUSE_CTRL1_CMD                                0x00000f08
+#define BNX2_DMA_FUSE_CTRL1_CMD_PWRUP_DONE              (1L<<0)
+#define BNX2_DMA_FUSE_CTRL1_CMD_SHIFT_DONE              (1L<<1)
+#define BNX2_DMA_FUSE_CTRL1_CMD_SHIFT                   (1L<<2)
+#define BNX2_DMA_FUSE_CTRL1_CMD_LOAD                    (1L<<3)
+#define BNX2_DMA_FUSE_CTRL1_CMD_SEL                     (0xfL<<8)
+
+#define BNX2_DMA_FUSE_CTRL1_DATA                       0x00000f0c
+#define BNX2_DMA_FUSE_CTRL2_CMD                                0x00000f10
+#define BNX2_DMA_FUSE_CTRL2_CMD_PWRUP_DONE              (1L<<0)
+#define BNX2_DMA_FUSE_CTRL2_CMD_SHIFT_DONE              (1L<<1)
+#define BNX2_DMA_FUSE_CTRL2_CMD_SHIFT                   (1L<<2)
+#define BNX2_DMA_FUSE_CTRL2_CMD_LOAD                    (1L<<3)
+#define BNX2_DMA_FUSE_CTRL2_CMD_SEL                     (0xfL<<8)
+
+#define BNX2_DMA_FUSE_CTRL2_DATA                       0x00000f14
+
+
+/*
+ *  context_reg definition
+ *  offset: 0x1000
+ */
+#define BNX2_CTX_COMMAND                               0x00001000
+#define BNX2_CTX_COMMAND_ENABLED                        (1L<<0)
+
+#define BNX2_CTX_STATUS                                        0x00001004
+#define BNX2_CTX_STATUS_LOCK_WAIT                       (1L<<0)
+#define BNX2_CTX_STATUS_READ_STAT                       (1L<<16)
+#define BNX2_CTX_STATUS_WRITE_STAT                      (1L<<17)
+#define BNX2_CTX_STATUS_ACC_STALL_STAT                  (1L<<18)
+#define BNX2_CTX_STATUS_LOCK_STALL_STAT                         (1L<<19)
+
+#define BNX2_CTX_VIRT_ADDR                             0x00001008
+#define BNX2_CTX_VIRT_ADDR_VIRT_ADDR                    (0x7fffL<<6)
+
+#define BNX2_CTX_PAGE_TBL                              0x0000100c
+#define BNX2_CTX_PAGE_TBL_PAGE_TBL                      (0x3fffL<<6)
+
+#define BNX2_CTX_DATA_ADR                              0x00001010
+#define BNX2_CTX_DATA_ADR_DATA_ADR                      (0x7ffffL<<2)
+
+#define BNX2_CTX_DATA                                  0x00001014
+#define BNX2_CTX_LOCK                                  0x00001018
+#define BNX2_CTX_LOCK_TYPE                              (0x7L<<0)
+#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_VOID               (0x0L<<0)
+#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_COMPLETE           (0x7L<<0)
+#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_PROTOCOL           (0x1L<<0)
+#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_TX                         (0x2L<<0)
+#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_TIMER              (0x4L<<0)
+#define BNX2_CTX_LOCK_CID_VALUE                                 (0x3fffL<<7)
+#define BNX2_CTX_LOCK_GRANTED                           (1L<<26)
+#define BNX2_CTX_LOCK_MODE                              (0x7L<<27)
+#define BNX2_CTX_LOCK_MODE_UNLOCK                       (0x0L<<27)
+#define BNX2_CTX_LOCK_MODE_IMMEDIATE                    (0x1L<<27)
+#define BNX2_CTX_LOCK_MODE_SURE                                 (0x2L<<27)
+#define BNX2_CTX_LOCK_STATUS                            (1L<<30)
+#define BNX2_CTX_LOCK_REQ                               (1L<<31)
+
+#define BNX2_CTX_ACCESS_STATUS                         0x00001040
+#define BNX2_CTX_ACCESS_STATUS_MASTERENCODED            (0xfL<<0)
+#define BNX2_CTX_ACCESS_STATUS_ACCESSMEMORYSM           (0x3L<<10)
+#define BNX2_CTX_ACCESS_STATUS_PAGETABLEINITSM          (0x3L<<12)
+#define BNX2_CTX_ACCESS_STATUS_ACCESSMEMORYINITSM       (0x3L<<14)
+#define BNX2_CTX_ACCESS_STATUS_QUALIFIED_REQUEST        (0x7ffL<<17)
+
+#define BNX2_CTX_DBG_LOCK_STATUS                       0x00001044
+#define BNX2_CTX_DBG_LOCK_STATUS_SM                     (0x3ffL<<0)
+#define BNX2_CTX_DBG_LOCK_STATUS_MATCH                  (0x3ffL<<22)
+
+#define BNX2_CTX_CHNL_LOCK_STATUS_0                    0x00001080
+#define BNX2_CTX_CHNL_LOCK_STATUS_0_CID                         (0x3fffL<<0)
+#define BNX2_CTX_CHNL_LOCK_STATUS_0_TYPE                (0x3L<<14)
+#define BNX2_CTX_CHNL_LOCK_STATUS_0_MODE                (1L<<16)
+
+#define BNX2_CTX_CHNL_LOCK_STATUS_1                    0x00001084
+#define BNX2_CTX_CHNL_LOCK_STATUS_2                    0x00001088
+#define BNX2_CTX_CHNL_LOCK_STATUS_3                    0x0000108c
+#define BNX2_CTX_CHNL_LOCK_STATUS_4                    0x00001090
+#define BNX2_CTX_CHNL_LOCK_STATUS_5                    0x00001094
+#define BNX2_CTX_CHNL_LOCK_STATUS_6                    0x00001098
+#define BNX2_CTX_CHNL_LOCK_STATUS_7                    0x0000109c
+#define BNX2_CTX_CHNL_LOCK_STATUS_8                    0x000010a0
+
+
+/*
+ *  emac_reg definition
+ *  offset: 0x1400
+ */
+#define BNX2_EMAC_MODE                                 0x00001400
+#define BNX2_EMAC_MODE_RESET                            (1L<<0)
+#define BNX2_EMAC_MODE_HALF_DUPLEX                      (1L<<1)
+#define BNX2_EMAC_MODE_PORT                             (0x3L<<2)
+#define BNX2_EMAC_MODE_PORT_NONE                        (0L<<2)
+#define BNX2_EMAC_MODE_PORT_MII                                 (1L<<2)
+#define BNX2_EMAC_MODE_PORT_GMII                        (2L<<2)
+#define BNX2_EMAC_MODE_PORT_UNDEF                       (3L<<2)
+#define BNX2_EMAC_MODE_MAC_LOOP                                 (1L<<4)
+#define BNX2_EMAC_MODE_TAGGED_MAC_CTL                   (1L<<7)
+#define BNX2_EMAC_MODE_TX_BURST                                 (1L<<8)
+#define BNX2_EMAC_MODE_MAX_DEFER_DROP_ENA               (1L<<9)
+#define BNX2_EMAC_MODE_EXT_LINK_POL                     (1L<<10)
+#define BNX2_EMAC_MODE_FORCE_LINK                       (1L<<11)
+#define BNX2_EMAC_MODE_MPKT                             (1L<<18)
+#define BNX2_EMAC_MODE_MPKT_RCVD                        (1L<<19)
+#define BNX2_EMAC_MODE_ACPI_RCVD                        (1L<<20)
+
+#define BNX2_EMAC_STATUS                               0x00001404
+#define BNX2_EMAC_STATUS_LINK                           (1L<<11)
+#define BNX2_EMAC_STATUS_LINK_CHANGE                    (1L<<12)
+#define BNX2_EMAC_STATUS_MI_COMPLETE                    (1L<<22)
+#define BNX2_EMAC_STATUS_MI_INT                                 (1L<<23)
+#define BNX2_EMAC_STATUS_AP_ERROR                       (1L<<24)
+#define BNX2_EMAC_STATUS_PARITY_ERROR_STATE             (1L<<31)
+
+#define BNX2_EMAC_ATTENTION_ENA                                0x00001408
+#define BNX2_EMAC_ATTENTION_ENA_LINK                    (1L<<11)
+#define BNX2_EMAC_ATTENTION_ENA_MI_COMPLETE             (1L<<22)
+#define BNX2_EMAC_ATTENTION_ENA_MI_INT                  (1L<<23)
+#define BNX2_EMAC_ATTENTION_ENA_AP_ERROR                (1L<<24)
+
+#define BNX2_EMAC_LED                                  0x0000140c
+#define BNX2_EMAC_LED_OVERRIDE                          (1L<<0)
+#define BNX2_EMAC_LED_1000MB_OVERRIDE                   (1L<<1)
+#define BNX2_EMAC_LED_100MB_OVERRIDE                    (1L<<2)
+#define BNX2_EMAC_LED_10MB_OVERRIDE                     (1L<<3)
+#define BNX2_EMAC_LED_TRAFFIC_OVERRIDE                  (1L<<4)
+#define BNX2_EMAC_LED_BLNK_TRAFFIC                      (1L<<5)
+#define BNX2_EMAC_LED_TRAFFIC                           (1L<<6)
+#define BNX2_EMAC_LED_1000MB                            (1L<<7)
+#define BNX2_EMAC_LED_100MB                             (1L<<8)
+#define BNX2_EMAC_LED_10MB                              (1L<<9)
+#define BNX2_EMAC_LED_TRAFFIC_STAT                      (1L<<10)
+#define BNX2_EMAC_LED_BLNK_RATE                                 (0xfffL<<19)
+#define BNX2_EMAC_LED_BLNK_RATE_ENA                     (1L<<31)
+
+#define BNX2_EMAC_MAC_MATCH0                           0x00001410
+#define BNX2_EMAC_MAC_MATCH1                           0x00001414
+#define BNX2_EMAC_MAC_MATCH2                           0x00001418
+#define BNX2_EMAC_MAC_MATCH3                           0x0000141c
+#define BNX2_EMAC_MAC_MATCH4                           0x00001420
+#define BNX2_EMAC_MAC_MATCH5                           0x00001424
+#define BNX2_EMAC_MAC_MATCH6                           0x00001428
+#define BNX2_EMAC_MAC_MATCH7                           0x0000142c
+#define BNX2_EMAC_MAC_MATCH8                           0x00001430
+#define BNX2_EMAC_MAC_MATCH9                           0x00001434
+#define BNX2_EMAC_MAC_MATCH10                          0x00001438
+#define BNX2_EMAC_MAC_MATCH11                          0x0000143c
+#define BNX2_EMAC_MAC_MATCH12                          0x00001440
+#define BNX2_EMAC_MAC_MATCH13                          0x00001444
+#define BNX2_EMAC_MAC_MATCH14                          0x00001448
+#define BNX2_EMAC_MAC_MATCH15                          0x0000144c
+#define BNX2_EMAC_MAC_MATCH16                          0x00001450
+#define BNX2_EMAC_MAC_MATCH17                          0x00001454
+#define BNX2_EMAC_MAC_MATCH18                          0x00001458
+#define BNX2_EMAC_MAC_MATCH19                          0x0000145c
+#define BNX2_EMAC_MAC_MATCH20                          0x00001460
+#define BNX2_EMAC_MAC_MATCH21                          0x00001464
+#define BNX2_EMAC_MAC_MATCH22                          0x00001468
+#define BNX2_EMAC_MAC_MATCH23                          0x0000146c
+#define BNX2_EMAC_MAC_MATCH24                          0x00001470
+#define BNX2_EMAC_MAC_MATCH25                          0x00001474
+#define BNX2_EMAC_MAC_MATCH26                          0x00001478
+#define BNX2_EMAC_MAC_MATCH27                          0x0000147c
+#define BNX2_EMAC_MAC_MATCH28                          0x00001480
+#define BNX2_EMAC_MAC_MATCH29                          0x00001484
+#define BNX2_EMAC_MAC_MATCH30                          0x00001488
+#define BNX2_EMAC_MAC_MATCH31                          0x0000148c
+#define BNX2_EMAC_BACKOFF_SEED                         0x00001498
+#define BNX2_EMAC_BACKOFF_SEED_EMAC_BACKOFF_SEED        (0x3ffL<<0)
+
+#define BNX2_EMAC_RX_MTU_SIZE                          0x0000149c
+#define BNX2_EMAC_RX_MTU_SIZE_MTU_SIZE                  (0xffffL<<0)
+#define BNX2_EMAC_RX_MTU_SIZE_JUMBO_ENA                         (1L<<31)
+
+#define BNX2_EMAC_SERDES_CNTL                          0x000014a4
+#define BNX2_EMAC_SERDES_CNTL_RXR                       (0x7L<<0)
+#define BNX2_EMAC_SERDES_CNTL_RXG                       (0x3L<<3)
+#define BNX2_EMAC_SERDES_CNTL_RXCKSEL                   (1L<<6)
+#define BNX2_EMAC_SERDES_CNTL_TXBIAS                    (0x7L<<7)
+#define BNX2_EMAC_SERDES_CNTL_BGMAX                     (1L<<10)
+#define BNX2_EMAC_SERDES_CNTL_BGMIN                     (1L<<11)
+#define BNX2_EMAC_SERDES_CNTL_TXMODE                    (1L<<12)
+#define BNX2_EMAC_SERDES_CNTL_TXEDGE                    (1L<<13)
+#define BNX2_EMAC_SERDES_CNTL_SERDES_MODE               (1L<<14)
+#define BNX2_EMAC_SERDES_CNTL_PLLTEST                   (1L<<15)
+#define BNX2_EMAC_SERDES_CNTL_CDET_EN                   (1L<<16)
+#define BNX2_EMAC_SERDES_CNTL_TBI_LBK                   (1L<<17)
+#define BNX2_EMAC_SERDES_CNTL_REMOTE_LBK                (1L<<18)
+#define BNX2_EMAC_SERDES_CNTL_REV_PHASE                         (1L<<19)
+#define BNX2_EMAC_SERDES_CNTL_REGCTL12                  (0x3L<<20)
+#define BNX2_EMAC_SERDES_CNTL_REGCTL25                  (0x3L<<22)
+
+#define BNX2_EMAC_SERDES_STATUS                                0x000014a8
+#define BNX2_EMAC_SERDES_STATUS_RX_STAT                         (0xffL<<0)
+#define BNX2_EMAC_SERDES_STATUS_COMMA_DET               (1L<<8)
+
+#define BNX2_EMAC_MDIO_COMM                            0x000014ac
+#define BNX2_EMAC_MDIO_COMM_DATA                        (0xffffL<<0)
+#define BNX2_EMAC_MDIO_COMM_REG_ADDR                    (0x1fL<<16)
+#define BNX2_EMAC_MDIO_COMM_PHY_ADDR                    (0x1fL<<21)
+#define BNX2_EMAC_MDIO_COMM_COMMAND                     (0x3L<<26)
+#define BNX2_EMAC_MDIO_COMM_COMMAND_UNDEFINED_0                 (0L<<26)
+#define BNX2_EMAC_MDIO_COMM_COMMAND_WRITE               (1L<<26)
+#define BNX2_EMAC_MDIO_COMM_COMMAND_READ                (2L<<26)
+#define BNX2_EMAC_MDIO_COMM_COMMAND_UNDEFINED_3                 (3L<<26)
+#define BNX2_EMAC_MDIO_COMM_FAIL                        (1L<<28)
+#define BNX2_EMAC_MDIO_COMM_START_BUSY                  (1L<<29)
+#define BNX2_EMAC_MDIO_COMM_DISEXT                      (1L<<30)
+
+#define BNX2_EMAC_MDIO_STATUS                          0x000014b0
+#define BNX2_EMAC_MDIO_STATUS_LINK                      (1L<<0)
+#define BNX2_EMAC_MDIO_STATUS_10MB                      (1L<<1)
+
+#define BNX2_EMAC_MDIO_MODE                            0x000014b4
+#define BNX2_EMAC_MDIO_MODE_SHORT_PREAMBLE              (1L<<1)
+#define BNX2_EMAC_MDIO_MODE_AUTO_POLL                   (1L<<4)
+#define BNX2_EMAC_MDIO_MODE_BIT_BANG                    (1L<<8)
+#define BNX2_EMAC_MDIO_MODE_MDIO                        (1L<<9)
+#define BNX2_EMAC_MDIO_MODE_MDIO_OE                     (1L<<10)
+#define BNX2_EMAC_MDIO_MODE_MDC                                 (1L<<11)
+#define BNX2_EMAC_MDIO_MODE_MDINT                       (1L<<12)
+#define BNX2_EMAC_MDIO_MODE_CLOCK_CNT                   (0x1fL<<16)
+
+#define BNX2_EMAC_MDIO_AUTO_STATUS                     0x000014b8
+#define BNX2_EMAC_MDIO_AUTO_STATUS_AUTO_ERR             (1L<<0)
+
+#define BNX2_EMAC_TX_MODE                              0x000014bc
+#define BNX2_EMAC_TX_MODE_RESET                                 (1L<<0)
+#define BNX2_EMAC_TX_MODE_EXT_PAUSE_EN                  (1L<<3)
+#define BNX2_EMAC_TX_MODE_FLOW_EN                       (1L<<4)
+#define BNX2_EMAC_TX_MODE_BIG_BACKOFF                   (1L<<5)
+#define BNX2_EMAC_TX_MODE_LONG_PAUSE                    (1L<<6)
+#define BNX2_EMAC_TX_MODE_LINK_AWARE                    (1L<<7)
+
+#define BNX2_EMAC_TX_STATUS                            0x000014c0
+#define BNX2_EMAC_TX_STATUS_XOFFED                      (1L<<0)
+#define BNX2_EMAC_TX_STATUS_XOFF_SENT                   (1L<<1)
+#define BNX2_EMAC_TX_STATUS_XON_SENT                    (1L<<2)
+#define BNX2_EMAC_TX_STATUS_LINK_UP                     (1L<<3)
+#define BNX2_EMAC_TX_STATUS_UNDERRUN                    (1L<<4)
+
+#define BNX2_EMAC_TX_LENGTHS                           0x000014c4
+#define BNX2_EMAC_TX_LENGTHS_SLOT                       (0xffL<<0)
+#define BNX2_EMAC_TX_LENGTHS_IPG                        (0xfL<<8)
+#define BNX2_EMAC_TX_LENGTHS_IPG_CRS                    (0x3L<<12)
+
+#define BNX2_EMAC_RX_MODE                              0x000014c8
+#define BNX2_EMAC_RX_MODE_RESET                                 (1L<<0)
+#define BNX2_EMAC_RX_MODE_FLOW_EN                       (1L<<2)
+#define BNX2_EMAC_RX_MODE_KEEP_MAC_CONTROL              (1L<<3)
+#define BNX2_EMAC_RX_MODE_KEEP_PAUSE                    (1L<<4)
+#define BNX2_EMAC_RX_MODE_ACCEPT_OVERSIZE               (1L<<5)
+#define BNX2_EMAC_RX_MODE_ACCEPT_RUNTS                  (1L<<6)
+#define BNX2_EMAC_RX_MODE_LLC_CHK                       (1L<<7)
+#define BNX2_EMAC_RX_MODE_PROMISCUOUS                   (1L<<8)
+#define BNX2_EMAC_RX_MODE_NO_CRC_CHK                    (1L<<9)
+#define BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG                         (1L<<10)
+#define BNX2_EMAC_RX_MODE_FILT_BROADCAST                (1L<<11)
+#define BNX2_EMAC_RX_MODE_SORT_MODE                     (1L<<12)
+
+#define BNX2_EMAC_RX_STATUS                            0x000014cc
+#define BNX2_EMAC_RX_STATUS_FFED                        (1L<<0)
+#define BNX2_EMAC_RX_STATUS_FF_RECEIVED                         (1L<<1)
+#define BNX2_EMAC_RX_STATUS_N_RECEIVED                  (1L<<2)
+
+#define BNX2_EMAC_MULTICAST_HASH0                      0x000014d0
+#define BNX2_EMAC_MULTICAST_HASH1                      0x000014d4
+#define BNX2_EMAC_MULTICAST_HASH2                      0x000014d8
+#define BNX2_EMAC_MULTICAST_HASH3                      0x000014dc
+#define BNX2_EMAC_MULTICAST_HASH4                      0x000014e0
+#define BNX2_EMAC_MULTICAST_HASH5                      0x000014e4
+#define BNX2_EMAC_MULTICAST_HASH6                      0x000014e8
+#define BNX2_EMAC_MULTICAST_HASH7                      0x000014ec
+#define BNX2_EMAC_RX_STAT_IFHCINOCTETS                 0x00001500
+#define BNX2_EMAC_RX_STAT_IFHCINBADOCTETS              0x00001504
+#define BNX2_EMAC_RX_STAT_ETHERSTATSFRAGMENTS          0x00001508
+#define BNX2_EMAC_RX_STAT_IFHCINUCASTPKTS              0x0000150c
+#define BNX2_EMAC_RX_STAT_IFHCINMULTICASTPKTS          0x00001510
+#define BNX2_EMAC_RX_STAT_IFHCINBROADCASTPKTS          0x00001514
+#define BNX2_EMAC_RX_STAT_DOT3STATSFCSERRORS           0x00001518
+#define BNX2_EMAC_RX_STAT_DOT3STATSALIGNMENTERRORS     0x0000151c
+#define BNX2_EMAC_RX_STAT_DOT3STATSCARRIERSENSEERRORS  0x00001520
+#define BNX2_EMAC_RX_STAT_XONPAUSEFRAMESRECEIVED       0x00001524
+#define BNX2_EMAC_RX_STAT_XOFFPAUSEFRAMESRECEIVED      0x00001528
+#define BNX2_EMAC_RX_STAT_MACCONTROLFRAMESRECEIVED     0x0000152c
+#define BNX2_EMAC_RX_STAT_XOFFSTATEENTERED             0x00001530
+#define BNX2_EMAC_RX_STAT_DOT3STATSFRAMESTOOLONG       0x00001534
+#define BNX2_EMAC_RX_STAT_ETHERSTATSJABBERS            0x00001538
+#define BNX2_EMAC_RX_STAT_ETHERSTATSUNDERSIZEPKTS      0x0000153c
+#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS64OCTETS       0x00001540
+#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS65OCTETSTO127OCTETS    0x00001544
+#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS128OCTETSTO255OCTETS   0x00001548
+#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS256OCTETSTO511OCTETS   0x0000154c
+#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS512OCTETSTO1023OCTETS  0x00001550
+#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS1024OCTETSTO1522OCTETS 0x00001554
+#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS1523OCTETSTO9022OCTETS 0x00001558
+#define BNX2_EMAC_RXMAC_DEBUG0                         0x0000155c
+#define BNX2_EMAC_RXMAC_DEBUG1                         0x00001560
+#define BNX2_EMAC_RXMAC_DEBUG1_LENGTH_NE_BYTE_COUNT     (1L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG1_LENGTH_OUT_RANGE                 (1L<<1)
+#define BNX2_EMAC_RXMAC_DEBUG1_BAD_CRC                  (1L<<2)
+#define BNX2_EMAC_RXMAC_DEBUG1_RX_ERROR                         (1L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG1_ALIGN_ERROR              (1L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG1_LAST_DATA                (1L<<5)
+#define BNX2_EMAC_RXMAC_DEBUG1_ODD_BYTE_START           (1L<<6)
+#define BNX2_EMAC_RXMAC_DEBUG1_BYTE_COUNT               (0xffffL<<7)
+#define BNX2_EMAC_RXMAC_DEBUG1_SLOT_TIME                (0xffL<<23)
+
+#define BNX2_EMAC_RXMAC_DEBUG2                         0x00001564
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE                         (0x7L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_IDLE            (0x0L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_SFD             (0x1L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_DATA            (0x2L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_SKEEP           (0x3L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_EXT             (0x4L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_DROP            (0x5L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_SDROP           (0x6L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_FC              (0x7L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE                (0xfL<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_IDLE           (0x0L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_DATA0          (0x1L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_DATA1          (0x2L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_DATA2          (0x3L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_DATA3          (0x4L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_ABORT          (0x5L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_WAIT           (0x6L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_STATUS                 (0x7L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_LAST           (0x8L<<3)
+#define BNX2_EMAC_RXMAC_DEBUG2_BYTE_IN                  (0xffL<<7)
+#define BNX2_EMAC_RXMAC_DEBUG2_FALSEC                   (1L<<15)
+#define BNX2_EMAC_RXMAC_DEBUG2_TAGGED                   (1L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG2_PAUSE_STATE              (1L<<18)
+#define BNX2_EMAC_RXMAC_DEBUG2_PAUSE_STATE_IDLE                 (0L<<18)
+#define BNX2_EMAC_RXMAC_DEBUG2_PAUSE_STATE_PAUSED       (1L<<18)
+#define BNX2_EMAC_RXMAC_DEBUG2_SE_COUNTER               (0xfL<<19)
+#define BNX2_EMAC_RXMAC_DEBUG2_QUANTA                   (0x1fL<<23)
+
+#define BNX2_EMAC_RXMAC_DEBUG3                         0x00001568
+#define BNX2_EMAC_RXMAC_DEBUG3_PAUSE_CTR                (0xffffL<<0)
+#define BNX2_EMAC_RXMAC_DEBUG3_TMP_PAUSE_CTR            (0xffffL<<16)
+
+#define BNX2_EMAC_RXMAC_DEBUG4                         0x0000156c
+#define BNX2_EMAC_RXMAC_DEBUG4_TYPE_FIELD               (0xffffL<<0)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE               (0x3fL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_IDLE          (0x0L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UMAC2                 (0x1L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UMAC3                 (0x2L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UNI           (0x3L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MMAC2                 (0x7L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MMAC3                 (0x5L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PSA1          (0x6L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PSA2          (0x7L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PSA3          (0x8L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MC2           (0x9L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MC3           (0xaL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MWAIT1        (0xeL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MWAIT2        (0xfL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MCHECK        (0x10L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MC            (0x11L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BC2           (0x12L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BC3           (0x13L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BSA1          (0x14L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BSA2          (0x15L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BSA3          (0x16L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BTYPE                 (0x17L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BC            (0x18L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PTYPE                 (0x19L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_CMD           (0x1aL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MAC           (0x1bL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_LATCH                 (0x1cL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_XOFF          (0x1dL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_XON           (0x1eL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PAUSED        (0x1fL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_NPAUSED       (0x20L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_TTYPE                 (0x21L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_TVAL          (0x22L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_USA1          (0x23L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_USA2          (0x24L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_USA3          (0x25L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UTYPE                 (0x26L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UTTYPE        (0x27L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UTVAL                 (0x28L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MTYPE                 (0x29L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_DROP          (0x2aL<<16)
+#define BNX2_EMAC_RXMAC_DEBUG4_DROP_PKT                         (1L<<22)
+#define BNX2_EMAC_RXMAC_DEBUG4_SLOT_FILLED              (1L<<23)
+#define BNX2_EMAC_RXMAC_DEBUG4_FALSE_CARRIER            (1L<<24)
+#define BNX2_EMAC_RXMAC_DEBUG4_LAST_DATA                (1L<<25)
+#define BNX2_EMAC_RXMAC_DEBUG4_sfd_FOUND                (1L<<26)
+#define BNX2_EMAC_RXMAC_DEBUG4_ADVANCE                  (1L<<27)
+#define BNX2_EMAC_RXMAC_DEBUG4_START                    (1L<<28)
+
+#define BNX2_EMAC_RXMAC_DEBUG5                         0x00001570
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM                         (0x7L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_IDLE            (0L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_WAIT_EOF        (1L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_WAIT_STAT       (2L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_SET_EOF4FCRC    (3L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_SET_EOF4RDE     (4L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_SET_EOF4ALL     (5L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_1WD_WAIT_STAT   (6L<<0)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1               (0x7L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_VDW           (0x0L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_STAT          (0x1L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_AEOF          (0x2L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_NEOF          (0x3L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_SOF           (0x4L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_SAEOF                 (0x6L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_SNEOF                 (0x7L<<4)
+#define BNX2_EMAC_RXMAC_DEBUG5_EOF_DETECTED             (1L<<7)
+#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF0               (0x7L<<8)
+#define BNX2_EMAC_RXMAC_DEBUG5_RPM_IDI_FIFO_FULL        (1L<<11)
+#define BNX2_EMAC_RXMAC_DEBUG5_LOAD_CCODE               (1L<<12)
+#define BNX2_EMAC_RXMAC_DEBUG5_LOAD_DATA                (1L<<13)
+#define BNX2_EMAC_RXMAC_DEBUG5_LOAD_STAT                (1L<<14)
+#define BNX2_EMAC_RXMAC_DEBUG5_CLR_STAT                         (1L<<15)
+#define BNX2_EMAC_RXMAC_DEBUG5_IDI_RPM_CCODE            (0x3L<<16)
+#define BNX2_EMAC_RXMAC_DEBUG5_IDI_RPM_ACCEPT           (1L<<19)
+#define BNX2_EMAC_RXMAC_DEBUG5_FMLEN                    (0xfffL<<20)
+
+#define BNX2_EMAC_RX_STAT_AC0                          0x00001580
+#define BNX2_EMAC_RX_STAT_AC1                          0x00001584
+#define BNX2_EMAC_RX_STAT_AC2                          0x00001588
+#define BNX2_EMAC_RX_STAT_AC3                          0x0000158c
+#define BNX2_EMAC_RX_STAT_AC4                          0x00001590
+#define BNX2_EMAC_RX_STAT_AC5                          0x00001594
+#define BNX2_EMAC_RX_STAT_AC6                          0x00001598
+#define BNX2_EMAC_RX_STAT_AC7                          0x0000159c
+#define BNX2_EMAC_RX_STAT_AC8                          0x000015a0
+#define BNX2_EMAC_RX_STAT_AC9                          0x000015a4
+#define BNX2_EMAC_RX_STAT_AC10                         0x000015a8
+#define BNX2_EMAC_RX_STAT_AC11                         0x000015ac
+#define BNX2_EMAC_RX_STAT_AC12                         0x000015b0
+#define BNX2_EMAC_RX_STAT_AC13                         0x000015b4
+#define BNX2_EMAC_RX_STAT_AC14                         0x000015b8
+#define BNX2_EMAC_RX_STAT_AC15                         0x000015bc
+#define BNX2_EMAC_RX_STAT_AC16                         0x000015c0
+#define BNX2_EMAC_RX_STAT_AC17                         0x000015c4
+#define BNX2_EMAC_RX_STAT_AC18                         0x000015c8
+#define BNX2_EMAC_RX_STAT_AC19                         0x000015cc
+#define BNX2_EMAC_RX_STAT_AC20                         0x000015d0
+#define BNX2_EMAC_RX_STAT_AC21                         0x000015d4
+#define BNX2_EMAC_RX_STAT_AC22                         0x000015d8
+#define BNX2_EMAC_RXMAC_SUC_DBG_OVERRUNVEC             0x000015dc
+#define BNX2_EMAC_TX_STAT_IFHCOUTOCTETS                        0x00001600
+#define BNX2_EMAC_TX_STAT_IFHCOUTBADOCTETS             0x00001604
+#define BNX2_EMAC_TX_STAT_ETHERSTATSCOLLISIONS         0x00001608
+#define BNX2_EMAC_TX_STAT_OUTXONSENT                   0x0000160c
+#define BNX2_EMAC_TX_STAT_OUTXOFFSENT                  0x00001610
+#define BNX2_EMAC_TX_STAT_FLOWCONTROLDONE              0x00001614
+#define BNX2_EMAC_TX_STAT_DOT3STATSSINGLECOLLISIONFRAMES       0x00001618
+#define BNX2_EMAC_TX_STAT_DOT3STATSMULTIPLECOLLISIONFRAMES     0x0000161c
+#define BNX2_EMAC_TX_STAT_DOT3STATSDEFERREDTRANSMISSIONS       0x00001620
+#define BNX2_EMAC_TX_STAT_DOT3STATSEXCESSIVECOLLISIONS 0x00001624
+#define BNX2_EMAC_TX_STAT_DOT3STATSLATECOLLISIONS      0x00001628
+#define BNX2_EMAC_TX_STAT_IFHCOUTUCASTPKTS             0x0000162c
+#define BNX2_EMAC_TX_STAT_IFHCOUTMULTICASTPKTS         0x00001630
+#define BNX2_EMAC_TX_STAT_IFHCOUTBROADCASTPKTS         0x00001634
+#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS64OCTETS       0x00001638
+#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS65OCTETSTO127OCTETS    0x0000163c
+#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS128OCTETSTO255OCTETS   0x00001640
+#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS256OCTETSTO511OCTETS   0x00001644
+#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS512OCTETSTO1023OCTETS  0x00001648
+#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS1024OCTETSTO1522OCTETS 0x0000164c
+#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS1523OCTETSTO9022OCTETS 0x00001650
+#define BNX2_EMAC_TX_STAT_DOT3STATSINTERNALMACTRANSMITERRORS   0x00001654
+#define BNX2_EMAC_TXMAC_DEBUG0                         0x00001658
+#define BNX2_EMAC_TXMAC_DEBUG1                         0x0000165c
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE                (0xfL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_IDLE           (0x0L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_START0                 (0x1L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_DATA0          (0x4L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_DATA1          (0x5L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_DATA2          (0x6L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_DATA3          (0x7L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_WAIT0          (0x8L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_WAIT1          (0x9L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG1_CRS_ENABLE               (1L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG1_BAD_CRC                  (1L<<5)
+#define BNX2_EMAC_TXMAC_DEBUG1_SE_COUNTER               (0xfL<<6)
+#define BNX2_EMAC_TXMAC_DEBUG1_SEND_PAUSE               (1L<<10)
+#define BNX2_EMAC_TXMAC_DEBUG1_LATE_COLLISION           (1L<<11)
+#define BNX2_EMAC_TXMAC_DEBUG1_MAX_DEFER                (1L<<12)
+#define BNX2_EMAC_TXMAC_DEBUG1_DEFERRED                         (1L<<13)
+#define BNX2_EMAC_TXMAC_DEBUG1_ONE_BYTE                         (1L<<14)
+#define BNX2_EMAC_TXMAC_DEBUG1_IPG_TIME                         (0xfL<<15)
+#define BNX2_EMAC_TXMAC_DEBUG1_SLOT_TIME                (0xffL<<19)
+
+#define BNX2_EMAC_TXMAC_DEBUG2                         0x00001660
+#define BNX2_EMAC_TXMAC_DEBUG2_BACK_OFF                         (0x3ffL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG2_BYTE_COUNT               (0xffffL<<10)
+#define BNX2_EMAC_TXMAC_DEBUG2_COL_COUNT                (0x1fL<<26)
+#define BNX2_EMAC_TXMAC_DEBUG2_COL_BIT                  (1L<<31)
+
+#define BNX2_EMAC_TXMAC_DEBUG3                         0x00001664
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE                         (0xfL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_IDLE            (0x0L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_PRE1            (0x1L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_PRE2            (0x2L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_SFD             (0x3L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_DATA            (0x4L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_CRC1            (0x5L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_CRC2            (0x6L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_EXT             (0x7L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_STATB           (0x8L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_STATG           (0x9L<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_JAM             (0xaL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_EJAM            (0xbL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_BJAM            (0xcL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_SWAIT           (0xdL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_BACKOFF                 (0xeL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE               (0x7L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_IDLE          (0x0L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_WAIT          (0x1L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_UNI           (0x2L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_MC            (0x3L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_BC2           (0x4L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_BC3           (0x5L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_BC            (0x6L<<4)
+#define BNX2_EMAC_TXMAC_DEBUG3_CRS_DONE                         (1L<<7)
+#define BNX2_EMAC_TXMAC_DEBUG3_XOFF                     (1L<<8)
+#define BNX2_EMAC_TXMAC_DEBUG3_SE_COUNTER               (0xfL<<9)
+#define BNX2_EMAC_TXMAC_DEBUG3_QUANTA_COUNTER           (0x1fL<<13)
+
+#define BNX2_EMAC_TXMAC_DEBUG4                         0x00001668
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_COUNTER            (0xffffL<<0)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE              (0xfL<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_IDLE                 (0x0L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_MCA1                 (0x2L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_MCA2                 (0x3L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_MCA3                 (0x6L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_SRC1                 (0x7L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_SRC2                 (0x5L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_SRC3                 (0x4L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_TYPE                 (0xcL<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_CMD          (0xeL<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_TIME                 (0xaL<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_CRC1                 (0x8L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_CRC2                 (0x9L<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_WAIT                 (0xdL<<16)
+#define BNX2_EMAC_TXMAC_DEBUG4_STATS0_VALID             (1L<<20)
+#define BNX2_EMAC_TXMAC_DEBUG4_APPEND_CRC               (1L<<21)
+#define BNX2_EMAC_TXMAC_DEBUG4_SLOT_FILLED              (1L<<22)
+#define BNX2_EMAC_TXMAC_DEBUG4_MAX_DEFER                (1L<<23)
+#define BNX2_EMAC_TXMAC_DEBUG4_SEND_EXTEND              (1L<<24)
+#define BNX2_EMAC_TXMAC_DEBUG4_SEND_PADDING             (1L<<25)
+#define BNX2_EMAC_TXMAC_DEBUG4_EOF_LOC                  (1L<<26)
+#define BNX2_EMAC_TXMAC_DEBUG4_COLLIDING                (1L<<27)
+#define BNX2_EMAC_TXMAC_DEBUG4_COL_IN                   (1L<<28)
+#define BNX2_EMAC_TXMAC_DEBUG4_BURSTING                         (1L<<29)
+#define BNX2_EMAC_TXMAC_DEBUG4_ADVANCE                  (1L<<30)
+#define BNX2_EMAC_TXMAC_DEBUG4_GO                       (1L<<31)
+
+#define BNX2_EMAC_TX_STAT_AC0                          0x00001680
+#define BNX2_EMAC_TX_STAT_AC1                          0x00001684
+#define BNX2_EMAC_TX_STAT_AC2                          0x00001688
+#define BNX2_EMAC_TX_STAT_AC3                          0x0000168c
+#define BNX2_EMAC_TX_STAT_AC4                          0x00001690
+#define BNX2_EMAC_TX_STAT_AC5                          0x00001694
+#define BNX2_EMAC_TX_STAT_AC6                          0x00001698
+#define BNX2_EMAC_TX_STAT_AC7                          0x0000169c
+#define BNX2_EMAC_TX_STAT_AC8                          0x000016a0
+#define BNX2_EMAC_TX_STAT_AC9                          0x000016a4
+#define BNX2_EMAC_TX_STAT_AC10                         0x000016a8
+#define BNX2_EMAC_TX_STAT_AC11                         0x000016ac
+#define BNX2_EMAC_TX_STAT_AC12                         0x000016b0
+#define BNX2_EMAC_TX_STAT_AC13                         0x000016b4
+#define BNX2_EMAC_TX_STAT_AC14                         0x000016b8
+#define BNX2_EMAC_TX_STAT_AC15                         0x000016bc
+#define BNX2_EMAC_TX_STAT_AC16                         0x000016c0
+#define BNX2_EMAC_TX_STAT_AC17                         0x000016c4
+#define BNX2_EMAC_TX_STAT_AC18                         0x000016c8
+#define BNX2_EMAC_TX_STAT_AC19                         0x000016cc
+#define BNX2_EMAC_TX_STAT_AC20                         0x000016d0
+#define BNX2_EMAC_TX_STAT_AC21                         0x000016d4
+#define BNX2_EMAC_TXMAC_SUC_DBG_OVERRUNVEC             0x000016d8
+
+
+/*
+ *  rpm_reg definition
+ *  offset: 0x1800
+ */
+#define BNX2_RPM_COMMAND                               0x00001800
+#define BNX2_RPM_COMMAND_ENABLED                        (1L<<0)
+#define BNX2_RPM_COMMAND_OVERRUN_ABORT                  (1L<<4)
+
+#define BNX2_RPM_STATUS                                        0x00001804
+#define BNX2_RPM_STATUS_MBUF_WAIT                       (1L<<0)
+#define BNX2_RPM_STATUS_FREE_WAIT                       (1L<<1)
+
+#define BNX2_RPM_CONFIG                                        0x00001808
+#define BNX2_RPM_CONFIG_NO_PSD_HDR_CKSUM                (1L<<0)
+#define BNX2_RPM_CONFIG_ACPI_ENA                        (1L<<1)
+#define BNX2_RPM_CONFIG_ACPI_KEEP                       (1L<<2)
+#define BNX2_RPM_CONFIG_MP_KEEP                                 (1L<<3)
+#define BNX2_RPM_CONFIG_SORT_VECT_VAL                   (0xfL<<4)
+#define BNX2_RPM_CONFIG_IGNORE_VLAN                     (1L<<31)
+
+#define BNX2_RPM_VLAN_MATCH0                           0x00001810
+#define BNX2_RPM_VLAN_MATCH0_RPM_VLAN_MTCH0_VALUE       (0xfffL<<0)
+
+#define BNX2_RPM_VLAN_MATCH1                           0x00001814
+#define BNX2_RPM_VLAN_MATCH1_RPM_VLAN_MTCH1_VALUE       (0xfffL<<0)
+
+#define BNX2_RPM_VLAN_MATCH2                           0x00001818
+#define BNX2_RPM_VLAN_MATCH2_RPM_VLAN_MTCH2_VALUE       (0xfffL<<0)
+
+#define BNX2_RPM_VLAN_MATCH3                           0x0000181c
+#define BNX2_RPM_VLAN_MATCH3_RPM_VLAN_MTCH3_VALUE       (0xfffL<<0)
+
+#define BNX2_RPM_SORT_USER0                            0x00001820
+#define BNX2_RPM_SORT_USER0_PM_EN                       (0xffffL<<0)
+#define BNX2_RPM_SORT_USER0_BC_EN                       (1L<<16)
+#define BNX2_RPM_SORT_USER0_MC_EN                       (1L<<17)
+#define BNX2_RPM_SORT_USER0_MC_HSH_EN                   (1L<<18)
+#define BNX2_RPM_SORT_USER0_PROM_EN                     (1L<<19)
+#define BNX2_RPM_SORT_USER0_VLAN_EN                     (0xfL<<20)
+#define BNX2_RPM_SORT_USER0_PROM_VLAN                   (1L<<24)
+#define BNX2_RPM_SORT_USER0_ENA                                 (1L<<31)
+
+#define BNX2_RPM_SORT_USER1                            0x00001824
+#define BNX2_RPM_SORT_USER1_PM_EN                       (0xffffL<<0)
+#define BNX2_RPM_SORT_USER1_BC_EN                       (1L<<16)
+#define BNX2_RPM_SORT_USER1_MC_EN                       (1L<<17)
+#define BNX2_RPM_SORT_USER1_MC_HSH_EN                   (1L<<18)
+#define BNX2_RPM_SORT_USER1_PROM_EN                     (1L<<19)
+#define BNX2_RPM_SORT_USER1_VLAN_EN                     (0xfL<<20)
+#define BNX2_RPM_SORT_USER1_PROM_VLAN                   (1L<<24)
+#define BNX2_RPM_SORT_USER1_ENA                                 (1L<<31)
+
+#define BNX2_RPM_SORT_USER2                            0x00001828
+#define BNX2_RPM_SORT_USER2_PM_EN                       (0xffffL<<0)
+#define BNX2_RPM_SORT_USER2_BC_EN                       (1L<<16)
+#define BNX2_RPM_SORT_USER2_MC_EN                       (1L<<17)
+#define BNX2_RPM_SORT_USER2_MC_HSH_EN                   (1L<<18)
+#define BNX2_RPM_SORT_USER2_PROM_EN                     (1L<<19)
+#define BNX2_RPM_SORT_USER2_VLAN_EN                     (0xfL<<20)
+#define BNX2_RPM_SORT_USER2_PROM_VLAN                   (1L<<24)
+#define BNX2_RPM_SORT_USER2_ENA                                 (1L<<31)
+
+#define BNX2_RPM_SORT_USER3                            0x0000182c
+#define BNX2_RPM_SORT_USER3_PM_EN                       (0xffffL<<0)
+#define BNX2_RPM_SORT_USER3_BC_EN                       (1L<<16)
+#define BNX2_RPM_SORT_USER3_MC_EN                       (1L<<17)
+#define BNX2_RPM_SORT_USER3_MC_HSH_EN                   (1L<<18)
+#define BNX2_RPM_SORT_USER3_PROM_EN                     (1L<<19)
+#define BNX2_RPM_SORT_USER3_VLAN_EN                     (0xfL<<20)
+#define BNX2_RPM_SORT_USER3_PROM_VLAN                   (1L<<24)
+#define BNX2_RPM_SORT_USER3_ENA                                 (1L<<31)
+
+#define BNX2_RPM_STAT_L2_FILTER_DISCARDS               0x00001840
+#define BNX2_RPM_STAT_RULE_CHECKER_DISCARDS            0x00001844
+#define BNX2_RPM_STAT_IFINFTQDISCARDS                  0x00001848
+#define BNX2_RPM_STAT_IFINMBUFDISCARD                  0x0000184c
+#define BNX2_RPM_STAT_RULE_CHECKER_P4_HIT              0x00001850
+#define BNX2_RPM_STAT_AC0                              0x00001880
+#define BNX2_RPM_STAT_AC1                              0x00001884
+#define BNX2_RPM_STAT_AC2                              0x00001888
+#define BNX2_RPM_STAT_AC3                              0x0000188c
+#define BNX2_RPM_STAT_AC4                              0x00001890
+#define BNX2_RPM_RC_CNTL_0                             0x00001900
+#define BNX2_RPM_RC_CNTL_0_OFFSET                       (0xffL<<0)
+#define BNX2_RPM_RC_CNTL_0_CLASS                        (0x7L<<8)
+#define BNX2_RPM_RC_CNTL_0_PRIORITY                     (1L<<11)
+#define BNX2_RPM_RC_CNTL_0_P4                           (1L<<12)
+#define BNX2_RPM_RC_CNTL_0_HDR_TYPE                     (0x7L<<13)
+#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_START               (0L<<13)
+#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_IP                  (1L<<13)
+#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_TCP                         (2L<<13)
+#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_UDP                         (3L<<13)
+#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_DATA                (4L<<13)
+#define BNX2_RPM_RC_CNTL_0_COMP                                 (0x3L<<16)
+#define BNX2_RPM_RC_CNTL_0_COMP_EQUAL                   (0L<<16)
+#define BNX2_RPM_RC_CNTL_0_COMP_NEQUAL                  (1L<<16)
+#define BNX2_RPM_RC_CNTL_0_COMP_GREATER                         (2L<<16)
+#define BNX2_RPM_RC_CNTL_0_COMP_LESS                    (3L<<16)
+#define BNX2_RPM_RC_CNTL_0_SBIT                                 (1L<<19)
+#define BNX2_RPM_RC_CNTL_0_CMDSEL                       (0xfL<<20)
+#define BNX2_RPM_RC_CNTL_0_MAP                          (1L<<24)
+#define BNX2_RPM_RC_CNTL_0_DISCARD                      (1L<<25)
+#define BNX2_RPM_RC_CNTL_0_MASK                                 (1L<<26)
+#define BNX2_RPM_RC_CNTL_0_P1                           (1L<<27)
+#define BNX2_RPM_RC_CNTL_0_P2                           (1L<<28)
+#define BNX2_RPM_RC_CNTL_0_P3                           (1L<<29)
+#define BNX2_RPM_RC_CNTL_0_NBIT                                 (1L<<30)
+
+#define BNX2_RPM_RC_VALUE_MASK_0                       0x00001904
+#define BNX2_RPM_RC_VALUE_MASK_0_VALUE                  (0xffffL<<0)
+#define BNX2_RPM_RC_VALUE_MASK_0_MASK                   (0xffffL<<16)
+
+#define BNX2_RPM_RC_CNTL_1                             0x00001908
+#define BNX2_RPM_RC_CNTL_1_A                            (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_1_B                            (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_1                       0x0000190c
+#define BNX2_RPM_RC_CNTL_2                             0x00001910
+#define BNX2_RPM_RC_CNTL_2_A                            (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_2_B                            (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_2                       0x00001914
+#define BNX2_RPM_RC_CNTL_3                             0x00001918
+#define BNX2_RPM_RC_CNTL_3_A                            (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_3_B                            (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_3                       0x0000191c
+#define BNX2_RPM_RC_CNTL_4                             0x00001920
+#define BNX2_RPM_RC_CNTL_4_A                            (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_4_B                            (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_4                       0x00001924
+#define BNX2_RPM_RC_CNTL_5                             0x00001928
+#define BNX2_RPM_RC_CNTL_5_A                            (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_5_B                            (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_5                       0x0000192c
+#define BNX2_RPM_RC_CNTL_6                             0x00001930
+#define BNX2_RPM_RC_CNTL_6_A                            (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_6_B                            (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_6                       0x00001934
+#define BNX2_RPM_RC_CNTL_7                             0x00001938
+#define BNX2_RPM_RC_CNTL_7_A                            (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_7_B                            (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_7                       0x0000193c
+#define BNX2_RPM_RC_CNTL_8                             0x00001940
+#define BNX2_RPM_RC_CNTL_8_A                            (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_8_B                            (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_8                       0x00001944
+#define BNX2_RPM_RC_CNTL_9                             0x00001948
+#define BNX2_RPM_RC_CNTL_9_A                            (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_9_B                            (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_9                       0x0000194c
+#define BNX2_RPM_RC_CNTL_10                            0x00001950
+#define BNX2_RPM_RC_CNTL_10_A                           (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_10_B                           (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_10                      0x00001954
+#define BNX2_RPM_RC_CNTL_11                            0x00001958
+#define BNX2_RPM_RC_CNTL_11_A                           (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_11_B                           (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_11                      0x0000195c
+#define BNX2_RPM_RC_CNTL_12                            0x00001960
+#define BNX2_RPM_RC_CNTL_12_A                           (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_12_B                           (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_12                      0x00001964
+#define BNX2_RPM_RC_CNTL_13                            0x00001968
+#define BNX2_RPM_RC_CNTL_13_A                           (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_13_B                           (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_13                      0x0000196c
+#define BNX2_RPM_RC_CNTL_14                            0x00001970
+#define BNX2_RPM_RC_CNTL_14_A                           (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_14_B                           (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_14                      0x00001974
+#define BNX2_RPM_RC_CNTL_15                            0x00001978
+#define BNX2_RPM_RC_CNTL_15_A                           (0x3ffffL<<0)
+#define BNX2_RPM_RC_CNTL_15_B                           (0xfffL<<19)
+
+#define BNX2_RPM_RC_VALUE_MASK_15                      0x0000197c
+#define BNX2_RPM_RC_CONFIG                             0x00001980
+#define BNX2_RPM_RC_CONFIG_RULE_ENABLE                  (0xffffL<<0)
+#define BNX2_RPM_RC_CONFIG_DEF_CLASS                    (0x7L<<24)
+
+#define BNX2_RPM_DEBUG0                                        0x00001984
+#define BNX2_RPM_DEBUG0_FM_BCNT                                 (0xffffL<<0)
+#define BNX2_RPM_DEBUG0_T_DATA_OFST_VLD                         (1L<<16)
+#define BNX2_RPM_DEBUG0_T_UDP_OFST_VLD                  (1L<<17)
+#define BNX2_RPM_DEBUG0_T_TCP_OFST_VLD                  (1L<<18)
+#define BNX2_RPM_DEBUG0_T_IP_OFST_VLD                   (1L<<19)
+#define BNX2_RPM_DEBUG0_IP_MORE_FRGMT                   (1L<<20)
+#define BNX2_RPM_DEBUG0_T_IP_NO_TCP_UDP_HDR             (1L<<21)
+#define BNX2_RPM_DEBUG0_LLC_SNAP                        (1L<<22)
+#define BNX2_RPM_DEBUG0_FM_STARTED                      (1L<<23)
+#define BNX2_RPM_DEBUG0_DONE                            (1L<<24)
+#define BNX2_RPM_DEBUG0_WAIT_4_DONE                     (1L<<25)
+#define BNX2_RPM_DEBUG0_USE_TPBUF_CKSUM                         (1L<<26)
+#define BNX2_RPM_DEBUG0_RX_NO_PSD_HDR_CKSUM             (1L<<27)
+#define BNX2_RPM_DEBUG0_IGNORE_VLAN                     (1L<<28)
+#define BNX2_RPM_DEBUG0_RP_ENA_ACTIVE                   (1L<<31)
+
+#define BNX2_RPM_DEBUG1                                        0x00001988
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST                      (0xffffL<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_IDLE                         (0L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ETYPE_B6_ALL                 (1L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ETYPE_B2_IPLLC       (2L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ETYPE_B6_IP          (4L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ETYPE_B2_IP          (8L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_IP_START             (16L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_IP                   (32L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_TCP                  (64L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_UDP                  (128L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_AH                   (256L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ESP                  (512L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ESP_PAYLOAD          (1024L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_DATA                         (2048L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ADD_CARRY            (0x2000L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ADD_CARRYOUT                 (0x4000L<<0)
+#define BNX2_RPM_DEBUG1_FSM_CUR_ST_LATCH_RESULT                 (0x8000L<<0)
+#define BNX2_RPM_DEBUG1_HDR_BCNT                        (0x7ffL<<16)
+#define BNX2_RPM_DEBUG1_UNKNOWN_ETYPE_D                         (1L<<28)
+#define BNX2_RPM_DEBUG1_VLAN_REMOVED_D2                         (1L<<29)
+#define BNX2_RPM_DEBUG1_VLAN_REMOVED_D1                         (1L<<30)
+#define BNX2_RPM_DEBUG1_EOF_0XTRA_WD                    (1L<<31)
+
+#define BNX2_RPM_DEBUG2                                        0x0000198c
+#define BNX2_RPM_DEBUG2_CMD_HIT_VEC                     (0xffffL<<0)
+#define BNX2_RPM_DEBUG2_IP_BCNT                                 (0xffL<<16)
+#define BNX2_RPM_DEBUG2_THIS_CMD_M4                     (1L<<24)
+#define BNX2_RPM_DEBUG2_THIS_CMD_M3                     (1L<<25)
+#define BNX2_RPM_DEBUG2_THIS_CMD_M2                     (1L<<26)
+#define BNX2_RPM_DEBUG2_THIS_CMD_M1                     (1L<<27)
+#define BNX2_RPM_DEBUG2_IPIPE_EMPTY                     (1L<<28)
+#define BNX2_RPM_DEBUG2_FM_DISCARD                      (1L<<29)
+#define BNX2_RPM_DEBUG2_LAST_RULE_IN_FM_D2              (1L<<30)
+#define BNX2_RPM_DEBUG2_LAST_RULE_IN_FM_D1              (1L<<31)
+
+#define BNX2_RPM_DEBUG3                                        0x00001990
+#define BNX2_RPM_DEBUG3_AVAIL_MBUF_PTR                  (0x1ffL<<0)
+#define BNX2_RPM_DEBUG3_RDE_RLUPQ_WR_REQ_INT            (1L<<9)
+#define BNX2_RPM_DEBUG3_RDE_RBUF_WR_LAST_INT            (1L<<10)
+#define BNX2_RPM_DEBUG3_RDE_RBUF_WR_REQ_INT             (1L<<11)
+#define BNX2_RPM_DEBUG3_RDE_RBUF_FREE_REQ               (1L<<12)
+#define BNX2_RPM_DEBUG3_RDE_RBUF_ALLOC_REQ              (1L<<13)
+#define BNX2_RPM_DEBUG3_DFSM_MBUF_NOTAVAIL              (1L<<14)
+#define BNX2_RPM_DEBUG3_RBUF_RDE_SOF_DROP               (1L<<15)
+#define BNX2_RPM_DEBUG3_DFIFO_VLD_ENTRY_CT              (0xfL<<16)
+#define BNX2_RPM_DEBUG3_RDE_SRC_FIFO_ALMFULL            (1L<<21)
+#define BNX2_RPM_DEBUG3_DROP_NXT_VLD                    (1L<<22)
+#define BNX2_RPM_DEBUG3_DROP_NXT                        (1L<<23)
+#define BNX2_RPM_DEBUG3_FTQ_FSM                                 (0x3L<<24)
+#define BNX2_RPM_DEBUG3_FTQ_FSM_IDLE                    (0x0L<<24)
+#define BNX2_RPM_DEBUG3_FTQ_FSM_WAIT_ACK                (0x1L<<24)
+#define BNX2_RPM_DEBUG3_FTQ_FSM_WAIT_FREE               (0x2L<<24)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM                     (0x3L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_SOF            (0x0L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_GET_MBUF            (0x1L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_DMA_DATA            (0x2L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_DATA           (0x3L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_EOF            (0x4L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_MF_ACK                 (0x5L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_DROP_NXT_VLD   (0x6L<<26)
+#define BNX2_RPM_DEBUG3_MBWRITE_FSM_DONE                (0x7L<<26)
+#define BNX2_RPM_DEBUG3_MBFREE_FSM                      (1L<<29)
+#define BNX2_RPM_DEBUG3_MBFREE_FSM_IDLE                         (0L<<29)
+#define BNX2_RPM_DEBUG3_MBFREE_FSM_WAIT_ACK             (1L<<29)
+#define BNX2_RPM_DEBUG3_MBALLOC_FSM                     (1L<<30)
+#define BNX2_RPM_DEBUG3_MBALLOC_FSM_ET_MBUF             (0x0L<<30)
+#define BNX2_RPM_DEBUG3_MBALLOC_FSM_IVE_MBUF            (0x1L<<30)
+#define BNX2_RPM_DEBUG3_CCODE_EOF_ERROR                         (1L<<31)
+
+#define BNX2_RPM_DEBUG4                                        0x00001994
+#define BNX2_RPM_DEBUG4_DFSM_MBUF_CLUSTER               (0x1ffffffL<<0)
+#define BNX2_RPM_DEBUG4_DFIFO_CUR_CCODE                         (0x7L<<25)
+#define BNX2_RPM_DEBUG4_MBWRITE_FSM                     (0x7L<<28)
+#define BNX2_RPM_DEBUG4_DFIFO_EMPTY                     (1L<<31)
+
+#define BNX2_RPM_DEBUG5                                        0x00001998
+#define BNX2_RPM_DEBUG5_RDROP_WPTR                      (0x1fL<<0)
+#define BNX2_RPM_DEBUG5_RDROP_ACPI_RPTR                         (0x1fL<<5)
+#define BNX2_RPM_DEBUG5_RDROP_MC_RPTR                   (0x1fL<<10)
+#define BNX2_RPM_DEBUG5_RDROP_RC_RPTR                   (0x1fL<<15)
+#define BNX2_RPM_DEBUG5_RDROP_ACPI_EMPTY                (1L<<20)
+#define BNX2_RPM_DEBUG5_RDROP_MC_EMPTY                  (1L<<21)
+#define BNX2_RPM_DEBUG5_RDROP_AEOF_VEC_AT_RDROP_MC_RPTR         (1L<<22)
+#define BNX2_RPM_DEBUG5_HOLDREG_WOL_DROP_INT            (1L<<23)
+#define BNX2_RPM_DEBUG5_HOLDREG_DISCARD                         (1L<<24)
+#define BNX2_RPM_DEBUG5_HOLDREG_MBUF_NOTAVAIL           (1L<<25)
+#define BNX2_RPM_DEBUG5_HOLDREG_MC_EMPTY                (1L<<26)
+#define BNX2_RPM_DEBUG5_HOLDREG_RC_EMPTY                (1L<<27)
+#define BNX2_RPM_DEBUG5_HOLDREG_FC_EMPTY                (1L<<28)
+#define BNX2_RPM_DEBUG5_HOLDREG_ACPI_EMPTY              (1L<<29)
+#define BNX2_RPM_DEBUG5_HOLDREG_FULL_T                  (1L<<30)
+#define BNX2_RPM_DEBUG5_HOLDREG_RD                      (1L<<31)
+
+#define BNX2_RPM_DEBUG6                                        0x0000199c
+#define BNX2_RPM_DEBUG6_ACPI_VEC                        (0xffffL<<0)
+#define BNX2_RPM_DEBUG6_VEC                             (0xffffL<<16)
+
+#define BNX2_RPM_DEBUG7                                        0x000019a0
+#define BNX2_RPM_DEBUG7_RPM_DBG7_LAST_CRC               (0xffffffffL<<0)
+
+#define BNX2_RPM_DEBUG8                                        0x000019a4
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM                     (0xfL<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_IDLE                (0L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_SOF_W1_ADDR                 (1L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_SOF_W2_ADDR                 (2L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_SOF_W3_ADDR                 (3L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_SOF_WAIT_THBUF      (4L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W3_DATA             (5L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W0_ADDR             (6L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W1_ADDR             (7L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W2_ADDR             (8L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W3_ADDR             (9L<<0)
+#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_WAIT_THBUF          (10L<<0)
+#define BNX2_RPM_DEBUG8_COMPARE_AT_W0                   (1L<<4)
+#define BNX2_RPM_DEBUG8_COMPARE_AT_W3_DATA              (1L<<5)
+#define BNX2_RPM_DEBUG8_COMPARE_AT_SOF_WAIT             (1L<<6)
+#define BNX2_RPM_DEBUG8_COMPARE_AT_SOF_W3               (1L<<7)
+#define BNX2_RPM_DEBUG8_COMPARE_AT_SOF_W2               (1L<<8)
+#define BNX2_RPM_DEBUG8_EOF_W_LTEQ6_VLDBYTES            (1L<<9)
+#define BNX2_RPM_DEBUG8_EOF_W_LTEQ4_VLDBYTES            (1L<<10)
+#define BNX2_RPM_DEBUG8_NXT_EOF_W_12_VLDBYTES           (1L<<11)
+#define BNX2_RPM_DEBUG8_EOF_DET                                 (1L<<12)
+#define BNX2_RPM_DEBUG8_SOF_DET                                 (1L<<13)
+#define BNX2_RPM_DEBUG8_WAIT_4_SOF                      (1L<<14)
+#define BNX2_RPM_DEBUG8_ALL_DONE                        (1L<<15)
+#define BNX2_RPM_DEBUG8_THBUF_ADDR                      (0x7fL<<16)
+#define BNX2_RPM_DEBUG8_BYTE_CTR                        (0xffL<<24)
+
+#define BNX2_RPM_DEBUG9                                        0x000019a8
+#define BNX2_RPM_DEBUG9_OUTFIFO_COUNT                   (0x7L<<0)
+#define BNX2_RPM_DEBUG9_RDE_ACPI_RDY                    (1L<<3)
+#define BNX2_RPM_DEBUG9_VLD_RD_ENTRY_CT                         (0x7L<<4)
+#define BNX2_RPM_DEBUG9_OUTFIFO_OVERRUN_OCCURRED        (1L<<28)
+#define BNX2_RPM_DEBUG9_INFIFO_OVERRUN_OCCURRED                 (1L<<29)
+#define BNX2_RPM_DEBUG9_ACPI_MATCH_INT                  (1L<<30)
+#define BNX2_RPM_DEBUG9_ACPI_ENABLE_SYN                         (1L<<31)
+
+#define BNX2_RPM_ACPI_DBG_BUF_W00                      0x000019c0
+#define BNX2_RPM_ACPI_DBG_BUF_W01                      0x000019c4
+#define BNX2_RPM_ACPI_DBG_BUF_W02                      0x000019c8
+#define BNX2_RPM_ACPI_DBG_BUF_W03                      0x000019cc
+#define BNX2_RPM_ACPI_DBG_BUF_W10                      0x000019d0
+#define BNX2_RPM_ACPI_DBG_BUF_W11                      0x000019d4
+#define BNX2_RPM_ACPI_DBG_BUF_W12                      0x000019d8
+#define BNX2_RPM_ACPI_DBG_BUF_W13                      0x000019dc
+#define BNX2_RPM_ACPI_DBG_BUF_W20                      0x000019e0
+#define BNX2_RPM_ACPI_DBG_BUF_W21                      0x000019e4
+#define BNX2_RPM_ACPI_DBG_BUF_W22                      0x000019e8
+#define BNX2_RPM_ACPI_DBG_BUF_W23                      0x000019ec
+#define BNX2_RPM_ACPI_DBG_BUF_W30                      0x000019f0
+#define BNX2_RPM_ACPI_DBG_BUF_W31                      0x000019f4
+#define BNX2_RPM_ACPI_DBG_BUF_W32                      0x000019f8
+#define BNX2_RPM_ACPI_DBG_BUF_W33                      0x000019fc
+
+
+/*
+ *  rbuf_reg definition
+ *  offset: 0x200000
+ */
+#define BNX2_RBUF_COMMAND                              0x00200000
+#define BNX2_RBUF_COMMAND_ENABLED                       (1L<<0)
+#define BNX2_RBUF_COMMAND_FREE_INIT                     (1L<<1)
+#define BNX2_RBUF_COMMAND_RAM_INIT                      (1L<<2)
+#define BNX2_RBUF_COMMAND_OVER_FREE                     (1L<<4)
+#define BNX2_RBUF_COMMAND_ALLOC_REQ                     (1L<<5)
+
+#define BNX2_RBUF_STATUS1                              0x00200004
+#define BNX2_RBUF_STATUS1_FREE_COUNT                    (0x3ffL<<0)
+
+#define BNX2_RBUF_STATUS2                              0x00200008
+#define BNX2_RBUF_STATUS2_FREE_TAIL                     (0x3ffL<<0)
+#define BNX2_RBUF_STATUS2_FREE_HEAD                     (0x3ffL<<16)
+
+#define BNX2_RBUF_CONFIG                               0x0020000c
+#define BNX2_RBUF_CONFIG_XOFF_TRIP                      (0x3ffL<<0)
+#define BNX2_RBUF_CONFIG_XON_TRIP                       (0x3ffL<<16)
+
+#define BNX2_RBUF_FW_BUF_ALLOC                         0x00200010
+#define BNX2_RBUF_FW_BUF_ALLOC_VALUE                    (0x1ffL<<7)
+
+#define BNX2_RBUF_FW_BUF_FREE                          0x00200014
+#define BNX2_RBUF_FW_BUF_FREE_COUNT                     (0x7fL<<0)
+#define BNX2_RBUF_FW_BUF_FREE_TAIL                      (0x1ffL<<7)
+#define BNX2_RBUF_FW_BUF_FREE_HEAD                      (0x1ffL<<16)
+
+#define BNX2_RBUF_FW_BUF_SEL                           0x00200018
+#define BNX2_RBUF_FW_BUF_SEL_COUNT                      (0x7fL<<0)
+#define BNX2_RBUF_FW_BUF_SEL_TAIL                       (0x1ffL<<7)
+#define BNX2_RBUF_FW_BUF_SEL_HEAD                       (0x1ffL<<16)
+
+#define BNX2_RBUF_CONFIG2                              0x0020001c
+#define BNX2_RBUF_CONFIG2_MAC_DROP_TRIP                         (0x3ffL<<0)
+#define BNX2_RBUF_CONFIG2_MAC_KEEP_TRIP                         (0x3ffL<<16)
+
+#define BNX2_RBUF_CONFIG3                              0x00200020
+#define BNX2_RBUF_CONFIG3_CU_DROP_TRIP                  (0x3ffL<<0)
+#define BNX2_RBUF_CONFIG3_CU_KEEP_TRIP                  (0x3ffL<<16)
+
+#define BNX2_RBUF_PKT_DATA                             0x00208000
+#define BNX2_RBUF_CLIST_DATA                           0x00210000
+#define BNX2_RBUF_BUF_DATA                             0x00220000
+
+
+/*
+ *  rv2p_reg definition
+ *  offset: 0x2800
+ */
+#define BNX2_RV2P_COMMAND                              0x00002800
+#define BNX2_RV2P_COMMAND_ENABLED                       (1L<<0)
+#define BNX2_RV2P_COMMAND_PROC1_INTRPT                  (1L<<1)
+#define BNX2_RV2P_COMMAND_PROC2_INTRPT                  (1L<<2)
+#define BNX2_RV2P_COMMAND_ABORT0                        (1L<<4)
+#define BNX2_RV2P_COMMAND_ABORT1                        (1L<<5)
+#define BNX2_RV2P_COMMAND_ABORT2                        (1L<<6)
+#define BNX2_RV2P_COMMAND_ABORT3                        (1L<<7)
+#define BNX2_RV2P_COMMAND_ABORT4                        (1L<<8)
+#define BNX2_RV2P_COMMAND_ABORT5                        (1L<<9)
+#define BNX2_RV2P_COMMAND_PROC1_RESET                   (1L<<16)
+#define BNX2_RV2P_COMMAND_PROC2_RESET                   (1L<<17)
+#define BNX2_RV2P_COMMAND_CTXIF_RESET                   (1L<<18)
+
+#define BNX2_RV2P_STATUS                               0x00002804
+#define BNX2_RV2P_STATUS_ALWAYS_0                       (1L<<0)
+#define BNX2_RV2P_STATUS_RV2P_GEN_STAT0_CNT             (1L<<8)
+#define BNX2_RV2P_STATUS_RV2P_GEN_STAT1_CNT             (1L<<9)
+#define BNX2_RV2P_STATUS_RV2P_GEN_STAT2_CNT             (1L<<10)
+#define BNX2_RV2P_STATUS_RV2P_GEN_STAT3_CNT             (1L<<11)
+#define BNX2_RV2P_STATUS_RV2P_GEN_STAT4_CNT             (1L<<12)
+#define BNX2_RV2P_STATUS_RV2P_GEN_STAT5_CNT             (1L<<13)
+
+#define BNX2_RV2P_CONFIG                               0x00002808
+#define BNX2_RV2P_CONFIG_STALL_PROC1                    (1L<<0)
+#define BNX2_RV2P_CONFIG_STALL_PROC2                    (1L<<1)
+#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT0          (1L<<8)
+#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT1          (1L<<9)
+#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT2          (1L<<10)
+#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT3          (1L<<11)
+#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT4          (1L<<12)
+#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT5          (1L<<13)
+#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT0          (1L<<16)
+#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT1          (1L<<17)
+#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT2          (1L<<18)
+#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT3          (1L<<19)
+#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT4          (1L<<20)
+#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT5          (1L<<21)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE                      (0xfL<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_256                  (0L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_512                  (1L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_1K                   (2L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_2K                   (3L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_4K                   (4L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_8K                   (5L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_16K                  (6L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_32K                  (7L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_64K                  (8L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_128K                         (9L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_256K                         (10L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_512K                         (11L<<24)
+#define BNX2_RV2P_CONFIG_PAGE_SIZE_1M                   (12L<<24)
+
+#define BNX2_RV2P_GEN_BFR_ADDR_0                       0x00002810
+#define BNX2_RV2P_GEN_BFR_ADDR_0_VALUE                  (0xffffL<<16)
+
+#define BNX2_RV2P_GEN_BFR_ADDR_1                       0x00002814
+#define BNX2_RV2P_GEN_BFR_ADDR_1_VALUE                  (0xffffL<<16)
+
+#define BNX2_RV2P_GEN_BFR_ADDR_2                       0x00002818
+#define BNX2_RV2P_GEN_BFR_ADDR_2_VALUE                  (0xffffL<<16)
+
+#define BNX2_RV2P_GEN_BFR_ADDR_3                       0x0000281c
+#define BNX2_RV2P_GEN_BFR_ADDR_3_VALUE                  (0xffffL<<16)
+
+#define BNX2_RV2P_INSTR_HIGH                           0x00002830
+#define BNX2_RV2P_INSTR_HIGH_HIGH                       (0x1fL<<0)
+
+#define BNX2_RV2P_INSTR_LOW                            0x00002834
+#define BNX2_RV2P_PROC1_ADDR_CMD                       0x00002838
+#define BNX2_RV2P_PROC1_ADDR_CMD_ADD                    (0x3ffL<<0)
+#define BNX2_RV2P_PROC1_ADDR_CMD_RDWR                   (1L<<31)
+
+#define BNX2_RV2P_PROC2_ADDR_CMD                       0x0000283c
+#define BNX2_RV2P_PROC2_ADDR_CMD_ADD                    (0x3ffL<<0)
+#define BNX2_RV2P_PROC2_ADDR_CMD_RDWR                   (1L<<31)
+
+#define BNX2_RV2P_PROC1_GRC_DEBUG                      0x00002840
+#define BNX2_RV2P_PROC2_GRC_DEBUG                      0x00002844
+#define BNX2_RV2P_GRC_PROC_DEBUG                       0x00002848
+#define BNX2_RV2P_DEBUG_VECT_PEEK                      0x0000284c
+#define BNX2_RV2P_DEBUG_VECT_PEEK_1_VALUE               (0x7ffL<<0)
+#define BNX2_RV2P_DEBUG_VECT_PEEK_1_PEEK_EN             (1L<<11)
+#define BNX2_RV2P_DEBUG_VECT_PEEK_1_SEL                         (0xfL<<12)
+#define BNX2_RV2P_DEBUG_VECT_PEEK_2_VALUE               (0x7ffL<<16)
+#define BNX2_RV2P_DEBUG_VECT_PEEK_2_PEEK_EN             (1L<<27)
+#define BNX2_RV2P_DEBUG_VECT_PEEK_2_SEL                         (0xfL<<28)
+
+#define BNX2_RV2P_PFTQ_DATA                            0x00002b40
+#define BNX2_RV2P_PFTQ_CMD                             0x00002b78
+#define BNX2_RV2P_PFTQ_CMD_OFFSET                       (0x3ffL<<0)
+#define BNX2_RV2P_PFTQ_CMD_WR_TOP                       (1L<<10)
+#define BNX2_RV2P_PFTQ_CMD_WR_TOP_0                     (0L<<10)
+#define BNX2_RV2P_PFTQ_CMD_WR_TOP_1                     (1L<<10)
+#define BNX2_RV2P_PFTQ_CMD_SFT_RESET                    (1L<<25)
+#define BNX2_RV2P_PFTQ_CMD_RD_DATA                      (1L<<26)
+#define BNX2_RV2P_PFTQ_CMD_ADD_INTERVEN                         (1L<<27)
+#define BNX2_RV2P_PFTQ_CMD_ADD_DATA                     (1L<<28)
+#define BNX2_RV2P_PFTQ_CMD_INTERVENE_CLR                (1L<<29)
+#define BNX2_RV2P_PFTQ_CMD_POP                          (1L<<30)
+#define BNX2_RV2P_PFTQ_CMD_BUSY                                 (1L<<31)
+
+#define BNX2_RV2P_PFTQ_CTL                             0x00002b7c
+#define BNX2_RV2P_PFTQ_CTL_INTERVENE                    (1L<<0)
+#define BNX2_RV2P_PFTQ_CTL_OVERFLOW                     (1L<<1)
+#define BNX2_RV2P_PFTQ_CTL_FORCE_INTERVENE              (1L<<2)
+#define BNX2_RV2P_PFTQ_CTL_MAX_DEPTH                    (0x3ffL<<12)
+#define BNX2_RV2P_PFTQ_CTL_CUR_DEPTH                    (0x3ffL<<22)
+
+#define BNX2_RV2P_TFTQ_DATA                            0x00002b80
+#define BNX2_RV2P_TFTQ_CMD                             0x00002bb8
+#define BNX2_RV2P_TFTQ_CMD_OFFSET                       (0x3ffL<<0)
+#define BNX2_RV2P_TFTQ_CMD_WR_TOP                       (1L<<10)
+#define BNX2_RV2P_TFTQ_CMD_WR_TOP_0                     (0L<<10)
+#define BNX2_RV2P_TFTQ_CMD_WR_TOP_1                     (1L<<10)
+#define BNX2_RV2P_TFTQ_CMD_SFT_RESET                    (1L<<25)
+#define BNX2_RV2P_TFTQ_CMD_RD_DATA                      (1L<<26)
+#define BNX2_RV2P_TFTQ_CMD_ADD_INTERVEN                         (1L<<27)
+#define BNX2_RV2P_TFTQ_CMD_ADD_DATA                     (1L<<28)
+#define BNX2_RV2P_TFTQ_CMD_INTERVENE_CLR                (1L<<29)
+#define BNX2_RV2P_TFTQ_CMD_POP                          (1L<<30)
+#define BNX2_RV2P_TFTQ_CMD_BUSY                                 (1L<<31)
+
+#define BNX2_RV2P_TFTQ_CTL                             0x00002bbc
+#define BNX2_RV2P_TFTQ_CTL_INTERVENE                    (1L<<0)
+#define BNX2_RV2P_TFTQ_CTL_OVERFLOW                     (1L<<1)
+#define BNX2_RV2P_TFTQ_CTL_FORCE_INTERVENE              (1L<<2)
+#define BNX2_RV2P_TFTQ_CTL_MAX_DEPTH                    (0x3ffL<<12)
+#define BNX2_RV2P_TFTQ_CTL_CUR_DEPTH                    (0x3ffL<<22)
+
+#define BNX2_RV2P_MFTQ_DATA                            0x00002bc0
+#define BNX2_RV2P_MFTQ_CMD                             0x00002bf8
+#define BNX2_RV2P_MFTQ_CMD_OFFSET                       (0x3ffL<<0)
+#define BNX2_RV2P_MFTQ_CMD_WR_TOP                       (1L<<10)
+#define BNX2_RV2P_MFTQ_CMD_WR_TOP_0                     (0L<<10)
+#define BNX2_RV2P_MFTQ_CMD_WR_TOP_1                     (1L<<10)
+#define BNX2_RV2P_MFTQ_CMD_SFT_RESET                    (1L<<25)
+#define BNX2_RV2P_MFTQ_CMD_RD_DATA                      (1L<<26)
+#define BNX2_RV2P_MFTQ_CMD_ADD_INTERVEN                         (1L<<27)
+#define BNX2_RV2P_MFTQ_CMD_ADD_DATA                     (1L<<28)
+#define BNX2_RV2P_MFTQ_CMD_INTERVENE_CLR                (1L<<29)
+#define BNX2_RV2P_MFTQ_CMD_POP                          (1L<<30)
+#define BNX2_RV2P_MFTQ_CMD_BUSY                                 (1L<<31)
+
+#define BNX2_RV2P_MFTQ_CTL                             0x00002bfc
+#define BNX2_RV2P_MFTQ_CTL_INTERVENE                    (1L<<0)
+#define BNX2_RV2P_MFTQ_CTL_OVERFLOW                     (1L<<1)
+#define BNX2_RV2P_MFTQ_CTL_FORCE_INTERVENE              (1L<<2)
+#define BNX2_RV2P_MFTQ_CTL_MAX_DEPTH                    (0x3ffL<<12)
+#define BNX2_RV2P_MFTQ_CTL_CUR_DEPTH                    (0x3ffL<<22)
+
+
+
+/*
+ *  mq_reg definition
+ *  offset: 0x3c00
+ */
+#define BNX2_MQ_COMMAND                                        0x00003c00
+#define BNX2_MQ_COMMAND_ENABLED                                 (1L<<0)
+#define BNX2_MQ_COMMAND_OVERFLOW                        (1L<<4)
+#define BNX2_MQ_COMMAND_WR_ERROR                        (1L<<5)
+#define BNX2_MQ_COMMAND_RD_ERROR                        (1L<<6)
+
+#define BNX2_MQ_STATUS                                 0x00003c04
+#define BNX2_MQ_STATUS_CTX_ACCESS_STAT                  (1L<<16)
+#define BNX2_MQ_STATUS_CTX_ACCESS64_STAT                (1L<<17)
+#define BNX2_MQ_STATUS_PCI_STALL_STAT                   (1L<<18)
+
+#define BNX2_MQ_CONFIG                                 0x00003c08
+#define BNX2_MQ_CONFIG_TX_HIGH_PRI                      (1L<<0)
+#define BNX2_MQ_CONFIG_HALT_DIS                                 (1L<<1)
+#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE                         (0x7L<<4)
+#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_256             (0L<<4)
+#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_512             (1L<<4)
+#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_1K              (2L<<4)
+#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_2K              (3L<<4)
+#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_4K              (4L<<4)
+#define BNX2_MQ_CONFIG_MAX_DEPTH                        (0x7fL<<8)
+#define BNX2_MQ_CONFIG_CUR_DEPTH                        (0x7fL<<20)
+
+#define BNX2_MQ_ENQUEUE1                               0x00003c0c
+#define BNX2_MQ_ENQUEUE1_OFFSET                                 (0x3fL<<2)
+#define BNX2_MQ_ENQUEUE1_CID                            (0x3fffL<<8)
+#define BNX2_MQ_ENQUEUE1_BYTE_MASK                      (0xfL<<24)
+#define BNX2_MQ_ENQUEUE1_KNL_MODE                       (1L<<28)
+
+#define BNX2_MQ_ENQUEUE2                               0x00003c10
+#define BNX2_MQ_BAD_WR_ADDR                            0x00003c14
+#define BNX2_MQ_BAD_RD_ADDR                            0x00003c18
+#define BNX2_MQ_KNL_BYP_WIND_START                     0x00003c1c
+#define BNX2_MQ_KNL_BYP_WIND_START_VALUE                (0xfffffL<<12)
+
+#define BNX2_MQ_KNL_WIND_END                           0x00003c20
+#define BNX2_MQ_KNL_WIND_END_VALUE                      (0xffffffL<<8)
+
+#define BNX2_MQ_KNL_WRITE_MASK1                                0x00003c24
+#define BNX2_MQ_KNL_TX_MASK1                           0x00003c28
+#define BNX2_MQ_KNL_CMD_MASK1                          0x00003c2c
+#define BNX2_MQ_KNL_COND_ENQUEUE_MASK1                 0x00003c30
+#define BNX2_MQ_KNL_RX_V2P_MASK1                       0x00003c34
+#define BNX2_MQ_KNL_WRITE_MASK2                                0x00003c38
+#define BNX2_MQ_KNL_TX_MASK2                           0x00003c3c
+#define BNX2_MQ_KNL_CMD_MASK2                          0x00003c40
+#define BNX2_MQ_KNL_COND_ENQUEUE_MASK2                 0x00003c44
+#define BNX2_MQ_KNL_RX_V2P_MASK2                       0x00003c48
+#define BNX2_MQ_KNL_BYP_WRITE_MASK1                    0x00003c4c
+#define BNX2_MQ_KNL_BYP_TX_MASK1                       0x00003c50
+#define BNX2_MQ_KNL_BYP_CMD_MASK1                      0x00003c54
+#define BNX2_MQ_KNL_BYP_COND_ENQUEUE_MASK1             0x00003c58
+#define BNX2_MQ_KNL_BYP_RX_V2P_MASK1                   0x00003c5c
+#define BNX2_MQ_KNL_BYP_WRITE_MASK2                    0x00003c60
+#define BNX2_MQ_KNL_BYP_TX_MASK2                       0x00003c64
+#define BNX2_MQ_KNL_BYP_CMD_MASK2                      0x00003c68
+#define BNX2_MQ_KNL_BYP_COND_ENQUEUE_MASK2             0x00003c6c
+#define BNX2_MQ_KNL_BYP_RX_V2P_MASK2                   0x00003c70
+#define BNX2_MQ_MEM_WR_ADDR                            0x00003c74
+#define BNX2_MQ_MEM_WR_ADDR_VALUE                       (0x3fL<<0)
+
+#define BNX2_MQ_MEM_WR_DATA0                           0x00003c78
+#define BNX2_MQ_MEM_WR_DATA0_VALUE                      (0xffffffffL<<0)
+
+#define BNX2_MQ_MEM_WR_DATA1                           0x00003c7c
+#define BNX2_MQ_MEM_WR_DATA1_VALUE                      (0xffffffffL<<0)
+
+#define BNX2_MQ_MEM_WR_DATA2                           0x00003c80
+#define BNX2_MQ_MEM_WR_DATA2_VALUE                      (0x3fffffffL<<0)
+
+#define BNX2_MQ_MEM_RD_ADDR                            0x00003c84
+#define BNX2_MQ_MEM_RD_ADDR_VALUE                       (0x3fL<<0)
+
+#define BNX2_MQ_MEM_RD_DATA0                           0x00003c88
+#define BNX2_MQ_MEM_RD_DATA0_VALUE                      (0xffffffffL<<0)
+
+#define BNX2_MQ_MEM_RD_DATA1                           0x00003c8c
+#define BNX2_MQ_MEM_RD_DATA1_VALUE                      (0xffffffffL<<0)
+
+#define BNX2_MQ_MEM_RD_DATA2                           0x00003c90
+#define BNX2_MQ_MEM_RD_DATA2_VALUE                      (0x3fffffffL<<0)
+
+
+
+/*
+ *  tbdr_reg definition
+ *  offset: 0x5000
+ */
+#define BNX2_TBDR_COMMAND                              0x00005000
+#define BNX2_TBDR_COMMAND_ENABLE                        (1L<<0)
+#define BNX2_TBDR_COMMAND_SOFT_RST                      (1L<<1)
+#define BNX2_TBDR_COMMAND_MSTR_ABORT                    (1L<<4)
+
+#define BNX2_TBDR_STATUS                               0x00005004
+#define BNX2_TBDR_STATUS_DMA_WAIT                       (1L<<0)
+#define BNX2_TBDR_STATUS_FTQ_WAIT                       (1L<<1)
+#define BNX2_TBDR_STATUS_FIFO_OVERFLOW                  (1L<<2)
+#define BNX2_TBDR_STATUS_FIFO_UNDERFLOW                         (1L<<3)
+#define BNX2_TBDR_STATUS_SEARCHMISS_ERROR               (1L<<4)
+#define BNX2_TBDR_STATUS_FTQ_ENTRY_CNT                  (1L<<5)
+#define BNX2_TBDR_STATUS_BURST_CNT                      (1L<<6)
+
+#define BNX2_TBDR_CONFIG                               0x00005008
+#define BNX2_TBDR_CONFIG_MAX_BDS                        (0xffL<<0)
+#define BNX2_TBDR_CONFIG_SWAP_MODE                      (1L<<8)
+#define BNX2_TBDR_CONFIG_PRIORITY                       (1L<<9)
+#define BNX2_TBDR_CONFIG_CACHE_NEXT_PAGE_PTRS           (1L<<10)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE                      (0xfL<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_256                  (0L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_512                  (1L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_1K                   (2L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_2K                   (3L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_4K                   (4L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_8K                   (5L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_16K                  (6L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_32K                  (7L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_64K                  (8L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_128K                         (9L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_256K                         (10L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_512K                         (11L<<24)
+#define BNX2_TBDR_CONFIG_PAGE_SIZE_1M                   (12L<<24)
+
+#define BNX2_TBDR_DEBUG_VECT_PEEK                      0x0000500c
+#define BNX2_TBDR_DEBUG_VECT_PEEK_1_VALUE               (0x7ffL<<0)
+#define BNX2_TBDR_DEBUG_VECT_PEEK_1_PEEK_EN             (1L<<11)
+#define BNX2_TBDR_DEBUG_VECT_PEEK_1_SEL                         (0xfL<<12)
+#define BNX2_TBDR_DEBUG_VECT_PEEK_2_VALUE               (0x7ffL<<16)
+#define BNX2_TBDR_DEBUG_VECT_PEEK_2_PEEK_EN             (1L<<27)
+#define BNX2_TBDR_DEBUG_VECT_PEEK_2_SEL                         (0xfL<<28)
+
+#define BNX2_TBDR_FTQ_DATA                             0x000053c0
+#define BNX2_TBDR_FTQ_CMD                              0x000053f8
+#define BNX2_TBDR_FTQ_CMD_OFFSET                        (0x3ffL<<0)
+#define BNX2_TBDR_FTQ_CMD_WR_TOP                        (1L<<10)
+#define BNX2_TBDR_FTQ_CMD_WR_TOP_0                      (0L<<10)
+#define BNX2_TBDR_FTQ_CMD_WR_TOP_1                      (1L<<10)
+#define BNX2_TBDR_FTQ_CMD_SFT_RESET                     (1L<<25)
+#define BNX2_TBDR_FTQ_CMD_RD_DATA                       (1L<<26)
+#define BNX2_TBDR_FTQ_CMD_ADD_INTERVEN                  (1L<<27)
+#define BNX2_TBDR_FTQ_CMD_ADD_DATA                      (1L<<28)
+#define BNX2_TBDR_FTQ_CMD_INTERVENE_CLR                         (1L<<29)
+#define BNX2_TBDR_FTQ_CMD_POP                           (1L<<30)
+#define BNX2_TBDR_FTQ_CMD_BUSY                          (1L<<31)
+
+#define BNX2_TBDR_FTQ_CTL                              0x000053fc
+#define BNX2_TBDR_FTQ_CTL_INTERVENE                     (1L<<0)
+#define BNX2_TBDR_FTQ_CTL_OVERFLOW                      (1L<<1)
+#define BNX2_TBDR_FTQ_CTL_FORCE_INTERVENE               (1L<<2)
+#define BNX2_TBDR_FTQ_CTL_MAX_DEPTH                     (0x3ffL<<12)
+#define BNX2_TBDR_FTQ_CTL_CUR_DEPTH                     (0x3ffL<<22)
+
+
+
+/*
+ *  tdma_reg definition
+ *  offset: 0x5c00
+ */
+#define BNX2_TDMA_COMMAND                              0x00005c00
+#define BNX2_TDMA_COMMAND_ENABLED                       (1L<<0)
+#define BNX2_TDMA_COMMAND_MASTER_ABORT                  (1L<<4)
+#define BNX2_TDMA_COMMAND_BAD_L2_LENGTH_ABORT           (1L<<7)
+
+#define BNX2_TDMA_STATUS                               0x00005c04
+#define BNX2_TDMA_STATUS_DMA_WAIT                       (1L<<0)
+#define BNX2_TDMA_STATUS_PAYLOAD_WAIT                   (1L<<1)
+#define BNX2_TDMA_STATUS_PATCH_FTQ_WAIT                         (1L<<2)
+#define BNX2_TDMA_STATUS_LOCK_WAIT                      (1L<<3)
+#define BNX2_TDMA_STATUS_FTQ_ENTRY_CNT                  (1L<<16)
+#define BNX2_TDMA_STATUS_BURST_CNT                      (1L<<17)
+
+#define BNX2_TDMA_CONFIG                               0x00005c08
+#define BNX2_TDMA_CONFIG_ONE_DMA                        (1L<<0)
+#define BNX2_TDMA_CONFIG_ONE_RECORD                     (1L<<1)
+#define BNX2_TDMA_CONFIG_LIMIT_SZ                       (0xfL<<4)
+#define BNX2_TDMA_CONFIG_LIMIT_SZ_64                    (0L<<4)
+#define BNX2_TDMA_CONFIG_LIMIT_SZ_128                   (0x4L<<4)
+#define BNX2_TDMA_CONFIG_LIMIT_SZ_256                   (0x6L<<4)
+#define BNX2_TDMA_CONFIG_LIMIT_SZ_512                   (0x8L<<4)
+#define BNX2_TDMA_CONFIG_LINE_SZ                        (0xfL<<8)
+#define BNX2_TDMA_CONFIG_LINE_SZ_64                     (0L<<8)
+#define BNX2_TDMA_CONFIG_LINE_SZ_128                    (4L<<8)
+#define BNX2_TDMA_CONFIG_LINE_SZ_256                    (6L<<8)
+#define BNX2_TDMA_CONFIG_LINE_SZ_512                    (8L<<8)
+#define BNX2_TDMA_CONFIG_ALIGN_ENA                      (1L<<15)
+#define BNX2_TDMA_CONFIG_CHK_L2_BD                      (1L<<16)
+#define BNX2_TDMA_CONFIG_FIFO_CMP                       (0xfL<<20)
+
+#define BNX2_TDMA_PAYLOAD_PROD                         0x00005c0c
+#define BNX2_TDMA_PAYLOAD_PROD_VALUE                    (0x1fffL<<3)
+
+#define BNX2_TDMA_DBG_WATCHDOG                         0x00005c10
+#define BNX2_TDMA_DBG_TRIGGER                          0x00005c14
+#define BNX2_TDMA_DMAD_FSM                             0x00005c80
+#define BNX2_TDMA_DMAD_FSM_BD_INVLD                     (1L<<0)
+#define BNX2_TDMA_DMAD_FSM_PUSH                                 (0xfL<<4)
+#define BNX2_TDMA_DMAD_FSM_ARB_TBDC                     (0x3L<<8)
+#define BNX2_TDMA_DMAD_FSM_ARB_CTX                      (1L<<12)
+#define BNX2_TDMA_DMAD_FSM_DR_INTF                      (1L<<16)
+#define BNX2_TDMA_DMAD_FSM_DMAD                                 (0x7L<<20)
+#define BNX2_TDMA_DMAD_FSM_BD                           (0xfL<<24)
+
+#define BNX2_TDMA_DMAD_STATUS                          0x00005c84
+#define BNX2_TDMA_DMAD_STATUS_RHOLD_PUSH_ENTRY          (0x3L<<0)
+#define BNX2_TDMA_DMAD_STATUS_RHOLD_DMAD_ENTRY          (0x3L<<4)
+#define BNX2_TDMA_DMAD_STATUS_RHOLD_BD_ENTRY            (0x3L<<8)
+#define BNX2_TDMA_DMAD_STATUS_IFTQ_ENUM                         (0xfL<<12)
+
+#define BNX2_TDMA_DR_INTF_FSM                          0x00005c88
+#define BNX2_TDMA_DR_INTF_FSM_L2_COMP                   (0x3L<<0)
+#define BNX2_TDMA_DR_INTF_FSM_TPATQ                     (0x7L<<4)
+#define BNX2_TDMA_DR_INTF_FSM_TPBUF                     (0x3L<<8)
+#define BNX2_TDMA_DR_INTF_FSM_DR_BUF                    (0x7L<<12)
+#define BNX2_TDMA_DR_INTF_FSM_DMAD                      (0x7L<<16)
+
+#define BNX2_TDMA_DR_INTF_STATUS                       0x00005c8c
+#define BNX2_TDMA_DR_INTF_STATUS_HOLE_PHASE             (0x7L<<0)
+#define BNX2_TDMA_DR_INTF_STATUS_DATA_AVAIL             (0x3L<<4)
+#define BNX2_TDMA_DR_INTF_STATUS_SHIFT_ADDR             (0x7L<<8)
+#define BNX2_TDMA_DR_INTF_STATUS_NXT_PNTR               (0xfL<<12)
+#define BNX2_TDMA_DR_INTF_STATUS_BYTE_COUNT             (0x7L<<16)
+
+#define BNX2_TDMA_FTQ_DATA                             0x00005fc0
+#define BNX2_TDMA_FTQ_CMD                              0x00005ff8
+#define BNX2_TDMA_FTQ_CMD_OFFSET                        (0x3ffL<<0)
+#define BNX2_TDMA_FTQ_CMD_WR_TOP                        (1L<<10)
+#define BNX2_TDMA_FTQ_CMD_WR_TOP_0                      (0L<<10)
+#define BNX2_TDMA_FTQ_CMD_WR_TOP_1                      (1L<<10)
+#define BNX2_TDMA_FTQ_CMD_SFT_RESET                     (1L<<25)
+#define BNX2_TDMA_FTQ_CMD_RD_DATA                       (1L<<26)
+#define BNX2_TDMA_FTQ_CMD_ADD_INTERVEN                  (1L<<27)
+#define BNX2_TDMA_FTQ_CMD_ADD_DATA                      (1L<<28)
+#define BNX2_TDMA_FTQ_CMD_INTERVENE_CLR                         (1L<<29)
+#define BNX2_TDMA_FTQ_CMD_POP                           (1L<<30)
+#define BNX2_TDMA_FTQ_CMD_BUSY                          (1L<<31)
+
+#define BNX2_TDMA_FTQ_CTL                              0x00005ffc
+#define BNX2_TDMA_FTQ_CTL_INTERVENE                     (1L<<0)
+#define BNX2_TDMA_FTQ_CTL_OVERFLOW                      (1L<<1)
+#define BNX2_TDMA_FTQ_CTL_FORCE_INTERVENE               (1L<<2)
+#define BNX2_TDMA_FTQ_CTL_MAX_DEPTH                     (0x3ffL<<12)
+#define BNX2_TDMA_FTQ_CTL_CUR_DEPTH                     (0x3ffL<<22)
+
+
+
+/*
+ *  hc_reg definition
+ *  offset: 0x6800
+ */
+#define BNX2_HC_COMMAND                                        0x00006800
+#define BNX2_HC_COMMAND_ENABLE                          (1L<<0)
+#define BNX2_HC_COMMAND_SKIP_ABORT                      (1L<<4)
+#define BNX2_HC_COMMAND_COAL_NOW                        (1L<<16)
+#define BNX2_HC_COMMAND_COAL_NOW_WO_INT                         (1L<<17)
+#define BNX2_HC_COMMAND_STATS_NOW                       (1L<<18)
+#define BNX2_HC_COMMAND_FORCE_INT                       (0x3L<<19)
+#define BNX2_HC_COMMAND_FORCE_INT_NULL                  (0L<<19)
+#define BNX2_HC_COMMAND_FORCE_INT_HIGH                  (1L<<19)
+#define BNX2_HC_COMMAND_FORCE_INT_LOW                   (2L<<19)
+#define BNX2_HC_COMMAND_FORCE_INT_FREE                  (3L<<19)
+#define BNX2_HC_COMMAND_CLR_STAT_NOW                    (1L<<21)
+
+#define BNX2_HC_STATUS                                 0x00006804
+#define BNX2_HC_STATUS_MASTER_ABORT                     (1L<<0)
+#define BNX2_HC_STATUS_PARITY_ERROR_STATE               (1L<<1)
+#define BNX2_HC_STATUS_PCI_CLK_CNT_STAT                         (1L<<16)
+#define BNX2_HC_STATUS_CORE_CLK_CNT_STAT                (1L<<17)
+#define BNX2_HC_STATUS_NUM_STATUS_BLOCKS_STAT           (1L<<18)
+#define BNX2_HC_STATUS_NUM_INT_GEN_STAT                         (1L<<19)
+#define BNX2_HC_STATUS_NUM_INT_MBOX_WR_STAT             (1L<<20)
+#define BNX2_HC_STATUS_CORE_CLKS_TO_HW_INTACK_STAT      (1L<<23)
+#define BNX2_HC_STATUS_CORE_CLKS_TO_SW_INTACK_STAT      (1L<<24)
+#define BNX2_HC_STATUS_CORE_CLKS_DURING_SW_INTACK_STAT  (1L<<25)
+
+#define BNX2_HC_CONFIG                                 0x00006808
+#define BNX2_HC_CONFIG_COLLECT_STATS                    (1L<<0)
+#define BNX2_HC_CONFIG_RX_TMR_MODE                      (1L<<1)
+#define BNX2_HC_CONFIG_TX_TMR_MODE                      (1L<<2)
+#define BNX2_HC_CONFIG_COM_TMR_MODE                     (1L<<3)
+#define BNX2_HC_CONFIG_CMD_TMR_MODE                     (1L<<4)
+#define BNX2_HC_CONFIG_STATISTIC_PRIORITY               (1L<<5)
+#define BNX2_HC_CONFIG_STATUS_PRIORITY                  (1L<<6)
+#define BNX2_HC_CONFIG_STAT_MEM_ADDR                    (0xffL<<8)
+
+#define BNX2_HC_ATTN_BITS_ENABLE                       0x0000680c
+#define BNX2_HC_STATUS_ADDR_L                          0x00006810
+#define BNX2_HC_STATUS_ADDR_H                          0x00006814
+#define BNX2_HC_STATISTICS_ADDR_L                      0x00006818
+#define BNX2_HC_STATISTICS_ADDR_H                      0x0000681c
+#define BNX2_HC_TX_QUICK_CONS_TRIP                     0x00006820
+#define BNX2_HC_TX_QUICK_CONS_TRIP_VALUE                (0xffL<<0)
+#define BNX2_HC_TX_QUICK_CONS_TRIP_INT                  (0xffL<<16)
+
+#define BNX2_HC_COMP_PROD_TRIP                         0x00006824
+#define BNX2_HC_COMP_PROD_TRIP_VALUE                    (0xffL<<0)
+#define BNX2_HC_COMP_PROD_TRIP_INT                      (0xffL<<16)
+
+#define BNX2_HC_RX_QUICK_CONS_TRIP                     0x00006828
+#define BNX2_HC_RX_QUICK_CONS_TRIP_VALUE                (0xffL<<0)
+#define BNX2_HC_RX_QUICK_CONS_TRIP_INT                  (0xffL<<16)
+
+#define BNX2_HC_RX_TICKS                               0x0000682c
+#define BNX2_HC_RX_TICKS_VALUE                          (0x3ffL<<0)
+#define BNX2_HC_RX_TICKS_INT                            (0x3ffL<<16)
+
+#define BNX2_HC_TX_TICKS                               0x00006830
+#define BNX2_HC_TX_TICKS_VALUE                          (0x3ffL<<0)
+#define BNX2_HC_TX_TICKS_INT                            (0x3ffL<<16)
+
+#define BNX2_HC_COM_TICKS                              0x00006834
+#define BNX2_HC_COM_TICKS_VALUE                                 (0x3ffL<<0)
+#define BNX2_HC_COM_TICKS_INT                           (0x3ffL<<16)
+
+#define BNX2_HC_CMD_TICKS                              0x00006838
+#define BNX2_HC_CMD_TICKS_VALUE                                 (0x3ffL<<0)
+#define BNX2_HC_CMD_TICKS_INT                           (0x3ffL<<16)
+
+#define BNX2_HC_PERIODIC_TICKS                         0x0000683c
+#define BNX2_HC_PERIODIC_TICKS_HC_PERIODIC_TICKS        (0xffffL<<0)
+
+#define BNX2_HC_STAT_COLLECT_TICKS                     0x00006840
+#define BNX2_HC_STAT_COLLECT_TICKS_HC_STAT_COLL_TICKS   (0xffL<<4)
+
+#define BNX2_HC_STATS_TICKS                            0x00006844
+#define BNX2_HC_STATS_TICKS_HC_STAT_TICKS               (0xffffL<<8)
+
+#define BNX2_HC_STAT_MEM_DATA                          0x0000684c
+#define BNX2_HC_STAT_GEN_SEL_0                         0x00006850
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0                (0x7fL<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT0      (0L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT1      (1L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT2      (2L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT3      (3L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT4      (4L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT5      (5L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT6      (6L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT7      (7L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT8      (8L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT9      (9L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT10     (10L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT11     (11L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT0      (12L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT1      (13L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT2      (14L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT3      (15L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT4      (16L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT5      (17L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT6      (18L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT7      (19L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT0      (20L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT1      (21L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT2      (22L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT3      (23L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT4      (24L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT5      (25L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT6      (26L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT7      (27L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT8      (28L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT9      (29L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT10     (30L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT11     (31L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPAT_STAT0     (32L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPAT_STAT1     (33L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPAT_STAT2     (34L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPAT_STAT3     (35L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT0       (36L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT1       (37L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT2       (38L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT3       (39L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT4       (40L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT5       (41L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT6       (42L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT7       (43L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT0      (44L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT1      (45L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT2      (46L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT3      (47L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT4      (48L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT5      (49L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT6      (50L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT7      (51L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_PCI_CLK_CNT    (52L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CORE_CLK_CNT   (53L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_NUM_STATUS_BLOCKS   (54L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_NUM_INT_GEN         (55L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_NUM_INT_MBOX_WR     (56L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_CORE_CLKS_TO_HW_INTACK      (59L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_CORE_CLKS_TO_SW_INTACK      (60L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_CORE_CLKS_DURING_SW_INTACK  (61L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TSCH_CMD_CNT   (62L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TSCH_SLOT_CNT  (63L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CSCH_CMD_CNT   (64L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CSCH_SLOT_CNT  (65L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RLUPQ_VALID_CNT        (66L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXPQ_VALID_CNT         (67L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXPCQ_VALID_CNT        (68L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2PPQ_VALID_CNT       (69L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2PMQ_VALID_CNT       (70L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2PTQ_VALID_CNT       (71L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RDMAQ_VALID_CNT        (72L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TSCHQ_VALID_CNT        (73L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TBDRQ_VALID_CNT        (74L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXPQ_VALID_CNT         (75L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TDMAQ_VALID_CNT        (76L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPATQ_VALID_CNT        (77L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TASQ_VALID_CNT         (78L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CSQ_VALID_CNT  (79L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CPQ_VALID_CNT  (80L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COMXQ_VALID_CNT        (81L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COMTQ_VALID_CNT        (82L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COMQ_VALID_CNT         (83L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MGMQ_VALID_CNT         (84L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_READ_TRANSFERS_CNT        (85L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_READ_DELAY_PCI_CLKS_CNT   (86L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_READ_TRANSFERS_CNT    (87L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_READ_DELAY_PCI_CLKS_CNT       (88L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_READ_RETRY_AFTER_DATA_CNT     (89L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_WRITE_TRANSFERS_CNT       (90L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_WRITE_DELAY_PCI_CLKS_CNT  (91L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_WRITE_TRANSFERS_CNT   (92L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_WRITE_DELAY_PCI_CLKS_CNT      (93L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_WRITE_RETRY_AFTER_DATA_CNT    (94L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CTX_WR_CNT64   (95L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CTX_RD_CNT64   (96L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CTX_ACC_STALL_CLKS     (97L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CTX_LOCK_STALL_CLKS    (98L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MBQ_CTX_ACCESS_STAT    (99L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MBQ_CTX_ACCESS64_STAT  (100L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MBQ_PCI_STALL_STAT     (101L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TBDR_FTQ_ENTRY_CNT     (102L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TBDR_BURST_CNT         (103L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TDMA_FTQ_ENTRY_CNT     (104L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TDMA_BURST_CNT         (105L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RDMA_FTQ_ENTRY_CNT     (106L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RDMA_BURST_CNT         (107L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RLUP_MATCH_CNT         (108L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_POLL_PASS_CNT      (109L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR1_CNT   (110L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR2_CNT   (111L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR3_CNT   (112L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR4_CNT   (113L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR5_CNT   (114L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT0     (115L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT1     (116L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT2     (117L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT3     (118L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT4     (119L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT5     (120L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RBDC_PROC1_MISS        (121L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RBDC_PROC2_MISS        (122L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RBDC_BURST_CNT         (127L<<0)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_1                (0x7fL<<8)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_2                (0x7fL<<16)
+#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_3                (0x7fL<<24)
+
+#define BNX2_HC_STAT_GEN_SEL_1                         0x00006854
+#define BNX2_HC_STAT_GEN_SEL_1_GEN_SEL_4                (0x7fL<<0)
+#define BNX2_HC_STAT_GEN_SEL_1_GEN_SEL_5                (0x7fL<<8)
+#define BNX2_HC_STAT_GEN_SEL_1_GEN_SEL_6                (0x7fL<<16)
+#define BNX2_HC_STAT_GEN_SEL_1_GEN_SEL_7                (0x7fL<<24)
+
+#define BNX2_HC_STAT_GEN_SEL_2                         0x00006858
+#define BNX2_HC_STAT_GEN_SEL_2_GEN_SEL_8                (0x7fL<<0)
+#define BNX2_HC_STAT_GEN_SEL_2_GEN_SEL_9                (0x7fL<<8)
+#define BNX2_HC_STAT_GEN_SEL_2_GEN_SEL_10               (0x7fL<<16)
+#define BNX2_HC_STAT_GEN_SEL_2_GEN_SEL_11               (0x7fL<<24)
+
+#define BNX2_HC_STAT_GEN_SEL_3                         0x0000685c
+#define BNX2_HC_STAT_GEN_SEL_3_GEN_SEL_12               (0x7fL<<0)
+#define BNX2_HC_STAT_GEN_SEL_3_GEN_SEL_13               (0x7fL<<8)
+#define BNX2_HC_STAT_GEN_SEL_3_GEN_SEL_14               (0x7fL<<16)
+#define BNX2_HC_STAT_GEN_SEL_3_GEN_SEL_15               (0x7fL<<24)
+
+#define BNX2_HC_STAT_GEN_STAT0                         0x00006888
+#define BNX2_HC_STAT_GEN_STAT1                         0x0000688c
+#define BNX2_HC_STAT_GEN_STAT2                         0x00006890
+#define BNX2_HC_STAT_GEN_STAT3                         0x00006894
+#define BNX2_HC_STAT_GEN_STAT4                         0x00006898
+#define BNX2_HC_STAT_GEN_STAT5                         0x0000689c
+#define BNX2_HC_STAT_GEN_STAT6                         0x000068a0
+#define BNX2_HC_STAT_GEN_STAT7                         0x000068a4
+#define BNX2_HC_STAT_GEN_STAT8                         0x000068a8
+#define BNX2_HC_STAT_GEN_STAT9                         0x000068ac
+#define BNX2_HC_STAT_GEN_STAT10                                0x000068b0
+#define BNX2_HC_STAT_GEN_STAT11                                0x000068b4
+#define BNX2_HC_STAT_GEN_STAT12                                0x000068b8
+#define BNX2_HC_STAT_GEN_STAT13                                0x000068bc
+#define BNX2_HC_STAT_GEN_STAT14                                0x000068c0
+#define BNX2_HC_STAT_GEN_STAT15                                0x000068c4
+#define BNX2_HC_STAT_GEN_STAT_AC0                      0x000068c8
+#define BNX2_HC_STAT_GEN_STAT_AC1                      0x000068cc
+#define BNX2_HC_STAT_GEN_STAT_AC2                      0x000068d0
+#define BNX2_HC_STAT_GEN_STAT_AC3                      0x000068d4
+#define BNX2_HC_STAT_GEN_STAT_AC4                      0x000068d8
+#define BNX2_HC_STAT_GEN_STAT_AC5                      0x000068dc
+#define BNX2_HC_STAT_GEN_STAT_AC6                      0x000068e0
+#define BNX2_HC_STAT_GEN_STAT_AC7                      0x000068e4
+#define BNX2_HC_STAT_GEN_STAT_AC8                      0x000068e8
+#define BNX2_HC_STAT_GEN_STAT_AC9                      0x000068ec
+#define BNX2_HC_STAT_GEN_STAT_AC10                     0x000068f0
+#define BNX2_HC_STAT_GEN_STAT_AC11                     0x000068f4
+#define BNX2_HC_STAT_GEN_STAT_AC12                     0x000068f8
+#define BNX2_HC_STAT_GEN_STAT_AC13                     0x000068fc
+#define BNX2_HC_STAT_GEN_STAT_AC14                     0x00006900
+#define BNX2_HC_STAT_GEN_STAT_AC15                     0x00006904
+#define BNX2_HC_VIS                                    0x00006908
+#define BNX2_HC_VIS_STAT_BUILD_STATE                    (0xfL<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_IDLE               (0L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_START              (1L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_REQUEST            (2L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_UPDATE64           (3L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_UPDATE32           (4L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_UPDATE_DONE        (5L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_DMA                (6L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_MSI_CONTROL        (7L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_MSI_LOW            (8L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_MSI_HIGH           (9L<<0)
+#define BNX2_HC_VIS_STAT_BUILD_STATE_MSI_DATA           (10L<<0)
+#define BNX2_HC_VIS_DMA_STAT_STATE                      (0xfL<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_IDLE                         (0L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_STATUS_PARAM                 (1L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_STATUS_DMA           (2L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_WRITE_COMP           (3L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_COMP                         (4L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_STATISTIC_PARAM      (5L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_STATISTIC_DMA        (6L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_WRITE_COMP_1                 (7L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_WRITE_COMP_2                 (8L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_WAIT                         (9L<<8)
+#define BNX2_HC_VIS_DMA_STAT_STATE_ABORT                (15L<<8)
+#define BNX2_HC_VIS_DMA_MSI_STATE                       (0x7L<<12)
+#define BNX2_HC_VIS_STATISTIC_DMA_EN_STATE              (0x3L<<15)
+#define BNX2_HC_VIS_STATISTIC_DMA_EN_STATE_IDLE                 (0L<<15)
+#define BNX2_HC_VIS_STATISTIC_DMA_EN_STATE_COUNT        (1L<<15)
+#define BNX2_HC_VIS_STATISTIC_DMA_EN_STATE_START        (2L<<15)
+
+#define BNX2_HC_VIS_1                                  0x0000690c
+#define BNX2_HC_VIS_1_HW_INTACK_STATE                   (1L<<4)
+#define BNX2_HC_VIS_1_HW_INTACK_STATE_IDLE              (0L<<4)
+#define BNX2_HC_VIS_1_HW_INTACK_STATE_COUNT             (1L<<4)
+#define BNX2_HC_VIS_1_SW_INTACK_STATE                   (1L<<5)
+#define BNX2_HC_VIS_1_SW_INTACK_STATE_IDLE              (0L<<5)
+#define BNX2_HC_VIS_1_SW_INTACK_STATE_COUNT             (1L<<5)
+#define BNX2_HC_VIS_1_DURING_SW_INTACK_STATE            (1L<<6)
+#define BNX2_HC_VIS_1_DURING_SW_INTACK_STATE_IDLE       (0L<<6)
+#define BNX2_HC_VIS_1_DURING_SW_INTACK_STATE_COUNT      (1L<<6)
+#define BNX2_HC_VIS_1_MAILBOX_COUNT_STATE               (1L<<7)
+#define BNX2_HC_VIS_1_MAILBOX_COUNT_STATE_IDLE          (0L<<7)
+#define BNX2_HC_VIS_1_MAILBOX_COUNT_STATE_COUNT                 (1L<<7)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE                  (0xfL<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_IDLE             (0L<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_DMA              (1L<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_UPDATE           (2L<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_ASSIGN           (3L<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_WAIT             (4L<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_REG_UPDATE       (5L<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_REG_ASSIGN       (6L<<17)
+#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_REG_WAIT                 (7L<<17)
+#define BNX2_HC_VIS_1_RAM_WR_ARB_STATE                  (0x3L<<21)
+#define BNX2_HC_VIS_1_RAM_WR_ARB_STATE_NORMAL           (0L<<21)
+#define BNX2_HC_VIS_1_RAM_WR_ARB_STATE_CLEAR            (1L<<21)
+#define BNX2_HC_VIS_1_INT_GEN_STATE                     (1L<<23)
+#define BNX2_HC_VIS_1_INT_GEN_STATE_DLE                         (0L<<23)
+#define BNX2_HC_VIS_1_INT_GEN_STATE_NTERRUPT            (1L<<23)
+#define BNX2_HC_VIS_1_STAT_CHAN_ID                      (0x7L<<24)
+#define BNX2_HC_VIS_1_INT_B                             (1L<<27)
+
+#define BNX2_HC_DEBUG_VECT_PEEK                                0x00006910
+#define BNX2_HC_DEBUG_VECT_PEEK_1_VALUE                         (0x7ffL<<0)
+#define BNX2_HC_DEBUG_VECT_PEEK_1_PEEK_EN               (1L<<11)
+#define BNX2_HC_DEBUG_VECT_PEEK_1_SEL                   (0xfL<<12)
+#define BNX2_HC_DEBUG_VECT_PEEK_2_VALUE                         (0x7ffL<<16)
+#define BNX2_HC_DEBUG_VECT_PEEK_2_PEEK_EN               (1L<<27)
+#define BNX2_HC_DEBUG_VECT_PEEK_2_SEL                   (0xfL<<28)
+
+
+
+/*
+ *  txp_reg definition
+ *  offset: 0x40000
+ */
+#define BNX2_TXP_CPU_MODE                              0x00045000
+#define BNX2_TXP_CPU_MODE_LOCAL_RST                     (1L<<0)
+#define BNX2_TXP_CPU_MODE_STEP_ENA                      (1L<<1)
+#define BNX2_TXP_CPU_MODE_PAGE_0_DATA_ENA               (1L<<2)
+#define BNX2_TXP_CPU_MODE_PAGE_0_INST_ENA               (1L<<3)
+#define BNX2_TXP_CPU_MODE_MSG_BIT1                      (1L<<6)
+#define BNX2_TXP_CPU_MODE_INTERRUPT_ENA                         (1L<<7)
+#define BNX2_TXP_CPU_MODE_SOFT_HALT                     (1L<<10)
+#define BNX2_TXP_CPU_MODE_BAD_DATA_HALT_ENA             (1L<<11)
+#define BNX2_TXP_CPU_MODE_BAD_INST_HALT_ENA             (1L<<12)
+#define BNX2_TXP_CPU_MODE_FIO_ABORT_HALT_ENA            (1L<<13)
+#define BNX2_TXP_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA       (1L<<15)
+
+#define BNX2_TXP_CPU_STATE                             0x00045004
+#define BNX2_TXP_CPU_STATE_BREAKPOINT                   (1L<<0)
+#define BNX2_TXP_CPU_STATE_BAD_INST_HALTED              (1L<<2)
+#define BNX2_TXP_CPU_STATE_PAGE_0_DATA_HALTED           (1L<<3)
+#define BNX2_TXP_CPU_STATE_PAGE_0_INST_HALTED           (1L<<4)
+#define BNX2_TXP_CPU_STATE_BAD_DATA_ADDR_HALTED                 (1L<<5)
+#define BNX2_TXP_CPU_STATE_BAD_pc_HALTED                (1L<<6)
+#define BNX2_TXP_CPU_STATE_ALIGN_HALTED                         (1L<<7)
+#define BNX2_TXP_CPU_STATE_FIO_ABORT_HALTED             (1L<<8)
+#define BNX2_TXP_CPU_STATE_SOFT_HALTED                  (1L<<10)
+#define BNX2_TXP_CPU_STATE_SPAD_UNDERFLOW               (1L<<11)
+#define BNX2_TXP_CPU_STATE_INTERRRUPT                   (1L<<12)
+#define BNX2_TXP_CPU_STATE_DATA_ACCESS_STALL            (1L<<14)
+#define BNX2_TXP_CPU_STATE_INST_FETCH_STALL             (1L<<15)
+#define BNX2_TXP_CPU_STATE_BLOCKED_READ                         (1L<<31)
+
+#define BNX2_TXP_CPU_EVENT_MASK                                0x00045008
+#define BNX2_TXP_CPU_EVENT_MASK_BREAKPOINT_MASK                 (1L<<0)
+#define BNX2_TXP_CPU_EVENT_MASK_BAD_INST_HALTED_MASK    (1L<<2)
+#define BNX2_TXP_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK         (1L<<3)
+#define BNX2_TXP_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK         (1L<<4)
+#define BNX2_TXP_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK       (1L<<5)
+#define BNX2_TXP_CPU_EVENT_MASK_BAD_PC_HALTED_MASK      (1L<<6)
+#define BNX2_TXP_CPU_EVENT_MASK_ALIGN_HALTED_MASK       (1L<<7)
+#define BNX2_TXP_CPU_EVENT_MASK_FIO_ABORT_MASK          (1L<<8)
+#define BNX2_TXP_CPU_EVENT_MASK_SOFT_HALTED_MASK        (1L<<10)
+#define BNX2_TXP_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK     (1L<<11)
+#define BNX2_TXP_CPU_EVENT_MASK_INTERRUPT_MASK          (1L<<12)
+
+#define BNX2_TXP_CPU_PROGRAM_COUNTER                   0x0004501c
+#define BNX2_TXP_CPU_INSTRUCTION                       0x00045020
+#define BNX2_TXP_CPU_DATA_ACCESS                       0x00045024
+#define BNX2_TXP_CPU_INTERRUPT_ENABLE                  0x00045028
+#define BNX2_TXP_CPU_INTERRUPT_VECTOR                  0x0004502c
+#define BNX2_TXP_CPU_INTERRUPT_SAVED_PC                        0x00045030
+#define BNX2_TXP_CPU_HW_BREAKPOINT                     0x00045034
+#define BNX2_TXP_CPU_HW_BREAKPOINT_DISABLE              (1L<<0)
+#define BNX2_TXP_CPU_HW_BREAKPOINT_ADDRESS              (0x3fffffffL<<2)
+
+#define BNX2_TXP_CPU_DEBUG_VECT_PEEK                   0x00045038
+#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_1_VALUE            (0x7ffL<<0)
+#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_1_PEEK_EN          (1L<<11)
+#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_1_SEL              (0xfL<<12)
+#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_2_VALUE            (0x7ffL<<16)
+#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_2_PEEK_EN          (1L<<27)
+#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_2_SEL              (0xfL<<28)
+
+#define BNX2_TXP_CPU_LAST_BRANCH_ADDR                  0x00045048
+#define BNX2_TXP_CPU_LAST_BRANCH_ADDR_TYPE              (1L<<1)
+#define BNX2_TXP_CPU_LAST_BRANCH_ADDR_TYPE_JUMP                 (0L<<1)
+#define BNX2_TXP_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH       (1L<<1)
+#define BNX2_TXP_CPU_LAST_BRANCH_ADDR_LBA               (0x3fffffffL<<2)
+
+#define BNX2_TXP_CPU_REG_FILE                          0x00045200
+#define BNX2_TXP_FTQ_DATA                              0x000453c0
+#define BNX2_TXP_FTQ_CMD                               0x000453f8
+#define BNX2_TXP_FTQ_CMD_OFFSET                                 (0x3ffL<<0)
+#define BNX2_TXP_FTQ_CMD_WR_TOP                                 (1L<<10)
+#define BNX2_TXP_FTQ_CMD_WR_TOP_0                       (0L<<10)
+#define BNX2_TXP_FTQ_CMD_WR_TOP_1                       (1L<<10)
+#define BNX2_TXP_FTQ_CMD_SFT_RESET                      (1L<<25)
+#define BNX2_TXP_FTQ_CMD_RD_DATA                        (1L<<26)
+#define BNX2_TXP_FTQ_CMD_ADD_INTERVEN                   (1L<<27)
+#define BNX2_TXP_FTQ_CMD_ADD_DATA                       (1L<<28)
+#define BNX2_TXP_FTQ_CMD_INTERVENE_CLR                  (1L<<29)
+#define BNX2_TXP_FTQ_CMD_POP                            (1L<<30)
+#define BNX2_TXP_FTQ_CMD_BUSY                           (1L<<31)
+
+#define BNX2_TXP_FTQ_CTL                               0x000453fc
+#define BNX2_TXP_FTQ_CTL_INTERVENE                      (1L<<0)
+#define BNX2_TXP_FTQ_CTL_OVERFLOW                       (1L<<1)
+#define BNX2_TXP_FTQ_CTL_FORCE_INTERVENE                (1L<<2)
+#define BNX2_TXP_FTQ_CTL_MAX_DEPTH                      (0x3ffL<<12)
+#define BNX2_TXP_FTQ_CTL_CUR_DEPTH                      (0x3ffL<<22)
+
+#define BNX2_TXP_SCRATCH                               0x00060000
+
+
+/*
+ *  tpat_reg definition
+ *  offset: 0x80000
+ */
+#define BNX2_TPAT_CPU_MODE                             0x00085000
+#define BNX2_TPAT_CPU_MODE_LOCAL_RST                    (1L<<0)
+#define BNX2_TPAT_CPU_MODE_STEP_ENA                     (1L<<1)
+#define BNX2_TPAT_CPU_MODE_PAGE_0_DATA_ENA              (1L<<2)
+#define BNX2_TPAT_CPU_MODE_PAGE_0_INST_ENA              (1L<<3)
+#define BNX2_TPAT_CPU_MODE_MSG_BIT1                     (1L<<6)
+#define BNX2_TPAT_CPU_MODE_INTERRUPT_ENA                (1L<<7)
+#define BNX2_TPAT_CPU_MODE_SOFT_HALT                    (1L<<10)
+#define BNX2_TPAT_CPU_MODE_BAD_DATA_HALT_ENA            (1L<<11)
+#define BNX2_TPAT_CPU_MODE_BAD_INST_HALT_ENA            (1L<<12)
+#define BNX2_TPAT_CPU_MODE_FIO_ABORT_HALT_ENA           (1L<<13)
+#define BNX2_TPAT_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA      (1L<<15)
+
+#define BNX2_TPAT_CPU_STATE                            0x00085004
+#define BNX2_TPAT_CPU_STATE_BREAKPOINT                  (1L<<0)
+#define BNX2_TPAT_CPU_STATE_BAD_INST_HALTED             (1L<<2)
+#define BNX2_TPAT_CPU_STATE_PAGE_0_DATA_HALTED          (1L<<3)
+#define BNX2_TPAT_CPU_STATE_PAGE_0_INST_HALTED          (1L<<4)
+#define BNX2_TPAT_CPU_STATE_BAD_DATA_ADDR_HALTED        (1L<<5)
+#define BNX2_TPAT_CPU_STATE_BAD_pc_HALTED               (1L<<6)
+#define BNX2_TPAT_CPU_STATE_ALIGN_HALTED                (1L<<7)
+#define BNX2_TPAT_CPU_STATE_FIO_ABORT_HALTED            (1L<<8)
+#define BNX2_TPAT_CPU_STATE_SOFT_HALTED                         (1L<<10)
+#define BNX2_TPAT_CPU_STATE_SPAD_UNDERFLOW              (1L<<11)
+#define BNX2_TPAT_CPU_STATE_INTERRRUPT                  (1L<<12)
+#define BNX2_TPAT_CPU_STATE_DATA_ACCESS_STALL           (1L<<14)
+#define BNX2_TPAT_CPU_STATE_INST_FETCH_STALL            (1L<<15)
+#define BNX2_TPAT_CPU_STATE_BLOCKED_READ                (1L<<31)
+
+#define BNX2_TPAT_CPU_EVENT_MASK                       0x00085008
+#define BNX2_TPAT_CPU_EVENT_MASK_BREAKPOINT_MASK        (1L<<0)
+#define BNX2_TPAT_CPU_EVENT_MASK_BAD_INST_HALTED_MASK   (1L<<2)
+#define BNX2_TPAT_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK        (1L<<3)
+#define BNX2_TPAT_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK        (1L<<4)
+#define BNX2_TPAT_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK      (1L<<5)
+#define BNX2_TPAT_CPU_EVENT_MASK_BAD_PC_HALTED_MASK     (1L<<6)
+#define BNX2_TPAT_CPU_EVENT_MASK_ALIGN_HALTED_MASK      (1L<<7)
+#define BNX2_TPAT_CPU_EVENT_MASK_FIO_ABORT_MASK                 (1L<<8)
+#define BNX2_TPAT_CPU_EVENT_MASK_SOFT_HALTED_MASK       (1L<<10)
+#define BNX2_TPAT_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK    (1L<<11)
+#define BNX2_TPAT_CPU_EVENT_MASK_INTERRUPT_MASK                 (1L<<12)
+
+#define BNX2_TPAT_CPU_PROGRAM_COUNTER                  0x0008501c
+#define BNX2_TPAT_CPU_INSTRUCTION                      0x00085020
+#define BNX2_TPAT_CPU_DATA_ACCESS                      0x00085024
+#define BNX2_TPAT_CPU_INTERRUPT_ENABLE                 0x00085028
+#define BNX2_TPAT_CPU_INTERRUPT_VECTOR                 0x0008502c
+#define BNX2_TPAT_CPU_INTERRUPT_SAVED_PC               0x00085030
+#define BNX2_TPAT_CPU_HW_BREAKPOINT                    0x00085034
+#define BNX2_TPAT_CPU_HW_BREAKPOINT_DISABLE             (1L<<0)
+#define BNX2_TPAT_CPU_HW_BREAKPOINT_ADDRESS             (0x3fffffffL<<2)
+
+#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK                  0x00085038
+#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_1_VALUE           (0x7ffL<<0)
+#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_1_PEEK_EN                 (1L<<11)
+#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_1_SEL             (0xfL<<12)
+#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_2_VALUE           (0x7ffL<<16)
+#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_2_PEEK_EN                 (1L<<27)
+#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_2_SEL             (0xfL<<28)
+
+#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR                 0x00085048
+#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR_TYPE             (1L<<1)
+#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR_TYPE_JUMP        (0L<<1)
+#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH      (1L<<1)
+#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR_LBA              (0x3fffffffL<<2)
+
+#define BNX2_TPAT_CPU_REG_FILE                         0x00085200
+#define BNX2_TPAT_FTQ_DATA                             0x000853c0
+#define BNX2_TPAT_FTQ_CMD                              0x000853f8
+#define BNX2_TPAT_FTQ_CMD_OFFSET                        (0x3ffL<<0)
+#define BNX2_TPAT_FTQ_CMD_WR_TOP                        (1L<<10)
+#define BNX2_TPAT_FTQ_CMD_WR_TOP_0                      (0L<<10)
+#define BNX2_TPAT_FTQ_CMD_WR_TOP_1                      (1L<<10)
+#define BNX2_TPAT_FTQ_CMD_SFT_RESET                     (1L<<25)
+#define BNX2_TPAT_FTQ_CMD_RD_DATA                       (1L<<26)
+#define BNX2_TPAT_FTQ_CMD_ADD_INTERVEN                  (1L<<27)
+#define BNX2_TPAT_FTQ_CMD_ADD_DATA                      (1L<<28)
+#define BNX2_TPAT_FTQ_CMD_INTERVENE_CLR                         (1L<<29)
+#define BNX2_TPAT_FTQ_CMD_POP                           (1L<<30)
+#define BNX2_TPAT_FTQ_CMD_BUSY                          (1L<<31)
+
+#define BNX2_TPAT_FTQ_CTL                              0x000853fc
+#define BNX2_TPAT_FTQ_CTL_INTERVENE                     (1L<<0)
+#define BNX2_TPAT_FTQ_CTL_OVERFLOW                      (1L<<1)
+#define BNX2_TPAT_FTQ_CTL_FORCE_INTERVENE               (1L<<2)
+#define BNX2_TPAT_FTQ_CTL_MAX_DEPTH                     (0x3ffL<<12)
+#define BNX2_TPAT_FTQ_CTL_CUR_DEPTH                     (0x3ffL<<22)
+
+#define BNX2_TPAT_SCRATCH                              0x000a0000
+
+
+/*
+ *  rxp_reg definition
+ *  offset: 0xc0000
+ */
+#define BNX2_RXP_CPU_MODE                              0x000c5000
+#define BNX2_RXP_CPU_MODE_LOCAL_RST                     (1L<<0)
+#define BNX2_RXP_CPU_MODE_STEP_ENA                      (1L<<1)
+#define BNX2_RXP_CPU_MODE_PAGE_0_DATA_ENA               (1L<<2)
+#define BNX2_RXP_CPU_MODE_PAGE_0_INST_ENA               (1L<<3)
+#define BNX2_RXP_CPU_MODE_MSG_BIT1                      (1L<<6)
+#define BNX2_RXP_CPU_MODE_INTERRUPT_ENA                         (1L<<7)
+#define BNX2_RXP_CPU_MODE_SOFT_HALT                     (1L<<10)
+#define BNX2_RXP_CPU_MODE_BAD_DATA_HALT_ENA             (1L<<11)
+#define BNX2_RXP_CPU_MODE_BAD_INST_HALT_ENA             (1L<<12)
+#define BNX2_RXP_CPU_MODE_FIO_ABORT_HALT_ENA            (1L<<13)
+#define BNX2_RXP_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA       (1L<<15)
+
+#define BNX2_RXP_CPU_STATE                             0x000c5004
+#define BNX2_RXP_CPU_STATE_BREAKPOINT                   (1L<<0)
+#define BNX2_RXP_CPU_STATE_BAD_INST_HALTED              (1L<<2)
+#define BNX2_RXP_CPU_STATE_PAGE_0_DATA_HALTED           (1L<<3)
+#define BNX2_RXP_CPU_STATE_PAGE_0_INST_HALTED           (1L<<4)
+#define BNX2_RXP_CPU_STATE_BAD_DATA_ADDR_HALTED                 (1L<<5)
+#define BNX2_RXP_CPU_STATE_BAD_pc_HALTED                (1L<<6)
+#define BNX2_RXP_CPU_STATE_ALIGN_HALTED                         (1L<<7)
+#define BNX2_RXP_CPU_STATE_FIO_ABORT_HALTED             (1L<<8)
+#define BNX2_RXP_CPU_STATE_SOFT_HALTED                  (1L<<10)
+#define BNX2_RXP_CPU_STATE_SPAD_UNDERFLOW               (1L<<11)
+#define BNX2_RXP_CPU_STATE_INTERRRUPT                   (1L<<12)
+#define BNX2_RXP_CPU_STATE_DATA_ACCESS_STALL            (1L<<14)
+#define BNX2_RXP_CPU_STATE_INST_FETCH_STALL             (1L<<15)
+#define BNX2_RXP_CPU_STATE_BLOCKED_READ                         (1L<<31)
+
+#define BNX2_RXP_CPU_EVENT_MASK                                0x000c5008
+#define BNX2_RXP_CPU_EVENT_MASK_BREAKPOINT_MASK                 (1L<<0)
+#define BNX2_RXP_CPU_EVENT_MASK_BAD_INST_HALTED_MASK    (1L<<2)
+#define BNX2_RXP_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK         (1L<<3)
+#define BNX2_RXP_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK         (1L<<4)
+#define BNX2_RXP_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK       (1L<<5)
+#define BNX2_RXP_CPU_EVENT_MASK_BAD_PC_HALTED_MASK      (1L<<6)
+#define BNX2_RXP_CPU_EVENT_MASK_ALIGN_HALTED_MASK       (1L<<7)
+#define BNX2_RXP_CPU_EVENT_MASK_FIO_ABORT_MASK          (1L<<8)
+#define BNX2_RXP_CPU_EVENT_MASK_SOFT_HALTED_MASK        (1L<<10)
+#define BNX2_RXP_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK     (1L<<11)
+#define BNX2_RXP_CPU_EVENT_MASK_INTERRUPT_MASK          (1L<<12)
+
+#define BNX2_RXP_CPU_PROGRAM_COUNTER                   0x000c501c
+#define BNX2_RXP_CPU_INSTRUCTION                       0x000c5020
+#define BNX2_RXP_CPU_DATA_ACCESS                       0x000c5024
+#define BNX2_RXP_CPU_INTERRUPT_ENABLE                  0x000c5028
+#define BNX2_RXP_CPU_INTERRUPT_VECTOR                  0x000c502c
+#define BNX2_RXP_CPU_INTERRUPT_SAVED_PC                        0x000c5030
+#define BNX2_RXP_CPU_HW_BREAKPOINT                     0x000c5034
+#define BNX2_RXP_CPU_HW_BREAKPOINT_DISABLE              (1L<<0)
+#define BNX2_RXP_CPU_HW_BREAKPOINT_ADDRESS              (0x3fffffffL<<2)
+
+#define BNX2_RXP_CPU_DEBUG_VECT_PEEK                   0x000c5038
+#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_1_VALUE            (0x7ffL<<0)
+#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_1_PEEK_EN          (1L<<11)
+#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_1_SEL              (0xfL<<12)
+#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_2_VALUE            (0x7ffL<<16)
+#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_2_PEEK_EN          (1L<<27)
+#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_2_SEL              (0xfL<<28)
+
+#define BNX2_RXP_CPU_LAST_BRANCH_ADDR                  0x000c5048
+#define BNX2_RXP_CPU_LAST_BRANCH_ADDR_TYPE              (1L<<1)
+#define BNX2_RXP_CPU_LAST_BRANCH_ADDR_TYPE_JUMP                 (0L<<1)
+#define BNX2_RXP_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH       (1L<<1)
+#define BNX2_RXP_CPU_LAST_BRANCH_ADDR_LBA               (0x3fffffffL<<2)
+
+#define BNX2_RXP_CPU_REG_FILE                          0x000c5200
+#define BNX2_RXP_CFTQ_DATA                             0x000c5380
+#define BNX2_RXP_CFTQ_CMD                              0x000c53b8
+#define BNX2_RXP_CFTQ_CMD_OFFSET                        (0x3ffL<<0)
+#define BNX2_RXP_CFTQ_CMD_WR_TOP                        (1L<<10)
+#define BNX2_RXP_CFTQ_CMD_WR_TOP_0                      (0L<<10)
+#define BNX2_RXP_CFTQ_CMD_WR_TOP_1                      (1L<<10)
+#define BNX2_RXP_CFTQ_CMD_SFT_RESET                     (1L<<25)
+#define BNX2_RXP_CFTQ_CMD_RD_DATA                       (1L<<26)
+#define BNX2_RXP_CFTQ_CMD_ADD_INTERVEN                  (1L<<27)
+#define BNX2_RXP_CFTQ_CMD_ADD_DATA                      (1L<<28)
+#define BNX2_RXP_CFTQ_CMD_INTERVENE_CLR                         (1L<<29)
+#define BNX2_RXP_CFTQ_CMD_POP                           (1L<<30)
+#define BNX2_RXP_CFTQ_CMD_BUSY                          (1L<<31)
+
+#define BNX2_RXP_CFTQ_CTL                              0x000c53bc
+#define BNX2_RXP_CFTQ_CTL_INTERVENE                     (1L<<0)
+#define BNX2_RXP_CFTQ_CTL_OVERFLOW                      (1L<<1)
+#define BNX2_RXP_CFTQ_CTL_FORCE_INTERVENE               (1L<<2)
+#define BNX2_RXP_CFTQ_CTL_MAX_DEPTH                     (0x3ffL<<12)
+#define BNX2_RXP_CFTQ_CTL_CUR_DEPTH                     (0x3ffL<<22)
+
+#define BNX2_RXP_FTQ_DATA                              0x000c53c0
+#define BNX2_RXP_FTQ_CMD                               0x000c53f8
+#define BNX2_RXP_FTQ_CMD_OFFSET                                 (0x3ffL<<0)
+#define BNX2_RXP_FTQ_CMD_WR_TOP                                 (1L<<10)
+#define BNX2_RXP_FTQ_CMD_WR_TOP_0                       (0L<<10)
+#define BNX2_RXP_FTQ_CMD_WR_TOP_1                       (1L<<10)
+#define BNX2_RXP_FTQ_CMD_SFT_RESET                      (1L<<25)
+#define BNX2_RXP_FTQ_CMD_RD_DATA                        (1L<<26)
+#define BNX2_RXP_FTQ_CMD_ADD_INTERVEN                   (1L<<27)
+#define BNX2_RXP_FTQ_CMD_ADD_DATA                       (1L<<28)
+#define BNX2_RXP_FTQ_CMD_INTERVENE_CLR                  (1L<<29)
+#define BNX2_RXP_FTQ_CMD_POP                            (1L<<30)
+#define BNX2_RXP_FTQ_CMD_BUSY                           (1L<<31)
+
+#define BNX2_RXP_FTQ_CTL                               0x000c53fc
+#define BNX2_RXP_FTQ_CTL_INTERVENE                      (1L<<0)
+#define BNX2_RXP_FTQ_CTL_OVERFLOW                       (1L<<1)
+#define BNX2_RXP_FTQ_CTL_FORCE_INTERVENE                (1L<<2)
+#define BNX2_RXP_FTQ_CTL_MAX_DEPTH                      (0x3ffL<<12)
+#define BNX2_RXP_FTQ_CTL_CUR_DEPTH                      (0x3ffL<<22)
+
+#define BNX2_RXP_SCRATCH                               0x000e0000
+
+
+/*
+ *  com_reg definition
+ *  offset: 0x100000
+ */
+#define BNX2_COM_CPU_MODE                              0x00105000
+#define BNX2_COM_CPU_MODE_LOCAL_RST                     (1L<<0)
+#define BNX2_COM_CPU_MODE_STEP_ENA                      (1L<<1)
+#define BNX2_COM_CPU_MODE_PAGE_0_DATA_ENA               (1L<<2)
+#define BNX2_COM_CPU_MODE_PAGE_0_INST_ENA               (1L<<3)
+#define BNX2_COM_CPU_MODE_MSG_BIT1                      (1L<<6)
+#define BNX2_COM_CPU_MODE_INTERRUPT_ENA                         (1L<<7)
+#define BNX2_COM_CPU_MODE_SOFT_HALT                     (1L<<10)
+#define BNX2_COM_CPU_MODE_BAD_DATA_HALT_ENA             (1L<<11)
+#define BNX2_COM_CPU_MODE_BAD_INST_HALT_ENA             (1L<<12)
+#define BNX2_COM_CPU_MODE_FIO_ABORT_HALT_ENA            (1L<<13)
+#define BNX2_COM_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA       (1L<<15)
+
+#define BNX2_COM_CPU_STATE                             0x00105004
+#define BNX2_COM_CPU_STATE_BREAKPOINT                   (1L<<0)
+#define BNX2_COM_CPU_STATE_BAD_INST_HALTED              (1L<<2)
+#define BNX2_COM_CPU_STATE_PAGE_0_DATA_HALTED           (1L<<3)
+#define BNX2_COM_CPU_STATE_PAGE_0_INST_HALTED           (1L<<4)
+#define BNX2_COM_CPU_STATE_BAD_DATA_ADDR_HALTED                 (1L<<5)
+#define BNX2_COM_CPU_STATE_BAD_pc_HALTED                (1L<<6)
+#define BNX2_COM_CPU_STATE_ALIGN_HALTED                         (1L<<7)
+#define BNX2_COM_CPU_STATE_FIO_ABORT_HALTED             (1L<<8)
+#define BNX2_COM_CPU_STATE_SOFT_HALTED                  (1L<<10)
+#define BNX2_COM_CPU_STATE_SPAD_UNDERFLOW               (1L<<11)
+#define BNX2_COM_CPU_STATE_INTERRRUPT                   (1L<<12)
+#define BNX2_COM_CPU_STATE_DATA_ACCESS_STALL            (1L<<14)
+#define BNX2_COM_CPU_STATE_INST_FETCH_STALL             (1L<<15)
+#define BNX2_COM_CPU_STATE_BLOCKED_READ                         (1L<<31)
+
+#define BNX2_COM_CPU_EVENT_MASK                                0x00105008
+#define BNX2_COM_CPU_EVENT_MASK_BREAKPOINT_MASK                 (1L<<0)
+#define BNX2_COM_CPU_EVENT_MASK_BAD_INST_HALTED_MASK    (1L<<2)
+#define BNX2_COM_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK         (1L<<3)
+#define BNX2_COM_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK         (1L<<4)
+#define BNX2_COM_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK       (1L<<5)
+#define BNX2_COM_CPU_EVENT_MASK_BAD_PC_HALTED_MASK      (1L<<6)
+#define BNX2_COM_CPU_EVENT_MASK_ALIGN_HALTED_MASK       (1L<<7)
+#define BNX2_COM_CPU_EVENT_MASK_FIO_ABORT_MASK          (1L<<8)
+#define BNX2_COM_CPU_EVENT_MASK_SOFT_HALTED_MASK        (1L<<10)
+#define BNX2_COM_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK     (1L<<11)
+#define BNX2_COM_CPU_EVENT_MASK_INTERRUPT_MASK          (1L<<12)
+
+#define BNX2_COM_CPU_PROGRAM_COUNTER                   0x0010501c
+#define BNX2_COM_CPU_INSTRUCTION                       0x00105020
+#define BNX2_COM_CPU_DATA_ACCESS                       0x00105024
+#define BNX2_COM_CPU_INTERRUPT_ENABLE                  0x00105028
+#define BNX2_COM_CPU_INTERRUPT_VECTOR                  0x0010502c
+#define BNX2_COM_CPU_INTERRUPT_SAVED_PC                        0x00105030
+#define BNX2_COM_CPU_HW_BREAKPOINT                     0x00105034
+#define BNX2_COM_CPU_HW_BREAKPOINT_DISABLE              (1L<<0)
+#define BNX2_COM_CPU_HW_BREAKPOINT_ADDRESS              (0x3fffffffL<<2)
+
+#define BNX2_COM_CPU_DEBUG_VECT_PEEK                   0x00105038
+#define BNX2_COM_CPU_DEBUG_VECT_PEEK_1_VALUE            (0x7ffL<<0)
+#define BNX2_COM_CPU_DEBUG_VECT_PEEK_1_PEEK_EN          (1L<<11)
+#define BNX2_COM_CPU_DEBUG_VECT_PEEK_1_SEL              (0xfL<<12)
+#define BNX2_COM_CPU_DEBUG_VECT_PEEK_2_VALUE            (0x7ffL<<16)
+#define BNX2_COM_CPU_DEBUG_VECT_PEEK_2_PEEK_EN          (1L<<27)
+#define BNX2_COM_CPU_DEBUG_VECT_PEEK_2_SEL              (0xfL<<28)
+
+#define BNX2_COM_CPU_LAST_BRANCH_ADDR                  0x00105048
+#define BNX2_COM_CPU_LAST_BRANCH_ADDR_TYPE              (1L<<1)
+#define BNX2_COM_CPU_LAST_BRANCH_ADDR_TYPE_JUMP                 (0L<<1)
+#define BNX2_COM_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH       (1L<<1)
+#define BNX2_COM_CPU_LAST_BRANCH_ADDR_LBA               (0x3fffffffL<<2)
+
+#define BNX2_COM_CPU_REG_FILE                          0x00105200
+#define BNX2_COM_COMXQ_FTQ_DATA                                0x00105340
+#define BNX2_COM_COMXQ_FTQ_CMD                         0x00105378
+#define BNX2_COM_COMXQ_FTQ_CMD_OFFSET                   (0x3ffL<<0)
+#define BNX2_COM_COMXQ_FTQ_CMD_WR_TOP                   (1L<<10)
+#define BNX2_COM_COMXQ_FTQ_CMD_WR_TOP_0                         (0L<<10)
+#define BNX2_COM_COMXQ_FTQ_CMD_WR_TOP_1                         (1L<<10)
+#define BNX2_COM_COMXQ_FTQ_CMD_SFT_RESET                (1L<<25)
+#define BNX2_COM_COMXQ_FTQ_CMD_RD_DATA                  (1L<<26)
+#define BNX2_COM_COMXQ_FTQ_CMD_ADD_INTERVEN             (1L<<27)
+#define BNX2_COM_COMXQ_FTQ_CMD_ADD_DATA                         (1L<<28)
+#define BNX2_COM_COMXQ_FTQ_CMD_INTERVENE_CLR            (1L<<29)
+#define BNX2_COM_COMXQ_FTQ_CMD_POP                      (1L<<30)
+#define BNX2_COM_COMXQ_FTQ_CMD_BUSY                     (1L<<31)
+
+#define BNX2_COM_COMXQ_FTQ_CTL                         0x0010537c
+#define BNX2_COM_COMXQ_FTQ_CTL_INTERVENE                (1L<<0)
+#define BNX2_COM_COMXQ_FTQ_CTL_OVERFLOW                         (1L<<1)
+#define BNX2_COM_COMXQ_FTQ_CTL_FORCE_INTERVENE          (1L<<2)
+#define BNX2_COM_COMXQ_FTQ_CTL_MAX_DEPTH                (0x3ffL<<12)
+#define BNX2_COM_COMXQ_FTQ_CTL_CUR_DEPTH                (0x3ffL<<22)
+
+#define BNX2_COM_COMTQ_FTQ_DATA                                0x00105380
+#define BNX2_COM_COMTQ_FTQ_CMD                         0x001053b8
+#define BNX2_COM_COMTQ_FTQ_CMD_OFFSET                   (0x3ffL<<0)
+#define BNX2_COM_COMTQ_FTQ_CMD_WR_TOP                   (1L<<10)
+#define BNX2_COM_COMTQ_FTQ_CMD_WR_TOP_0                         (0L<<10)
+#define BNX2_COM_COMTQ_FTQ_CMD_WR_TOP_1                         (1L<<10)
+#define BNX2_COM_COMTQ_FTQ_CMD_SFT_RESET                (1L<<25)
+#define BNX2_COM_COMTQ_FTQ_CMD_RD_DATA                  (1L<<26)
+#define BNX2_COM_COMTQ_FTQ_CMD_ADD_INTERVEN             (1L<<27)
+#define BNX2_COM_COMTQ_FTQ_CMD_ADD_DATA                         (1L<<28)
+#define BNX2_COM_COMTQ_FTQ_CMD_INTERVENE_CLR            (1L<<29)
+#define BNX2_COM_COMTQ_FTQ_CMD_POP                      (1L<<30)
+#define BNX2_COM_COMTQ_FTQ_CMD_BUSY                     (1L<<31)
+
+#define BNX2_COM_COMTQ_FTQ_CTL                         0x001053bc
+#define BNX2_COM_COMTQ_FTQ_CTL_INTERVENE                (1L<<0)
+#define BNX2_COM_COMTQ_FTQ_CTL_OVERFLOW                         (1L<<1)
+#define BNX2_COM_COMTQ_FTQ_CTL_FORCE_INTERVENE          (1L<<2)
+#define BNX2_COM_COMTQ_FTQ_CTL_MAX_DEPTH                (0x3ffL<<12)
+#define BNX2_COM_COMTQ_FTQ_CTL_CUR_DEPTH                (0x3ffL<<22)
+
+#define BNX2_COM_COMQ_FTQ_DATA                         0x001053c0
+#define BNX2_COM_COMQ_FTQ_CMD                          0x001053f8
+#define BNX2_COM_COMQ_FTQ_CMD_OFFSET                    (0x3ffL<<0)
+#define BNX2_COM_COMQ_FTQ_CMD_WR_TOP                    (1L<<10)
+#define BNX2_COM_COMQ_FTQ_CMD_WR_TOP_0                  (0L<<10)
+#define BNX2_COM_COMQ_FTQ_CMD_WR_TOP_1                  (1L<<10)
+#define BNX2_COM_COMQ_FTQ_CMD_SFT_RESET                         (1L<<25)
+#define BNX2_COM_COMQ_FTQ_CMD_RD_DATA                   (1L<<26)
+#define BNX2_COM_COMQ_FTQ_CMD_ADD_INTERVEN              (1L<<27)
+#define BNX2_COM_COMQ_FTQ_CMD_ADD_DATA                  (1L<<28)
+#define BNX2_COM_COMQ_FTQ_CMD_INTERVENE_CLR             (1L<<29)
+#define BNX2_COM_COMQ_FTQ_CMD_POP                       (1L<<30)
+#define BNX2_COM_COMQ_FTQ_CMD_BUSY                      (1L<<31)
+
+#define BNX2_COM_COMQ_FTQ_CTL                          0x001053fc
+#define BNX2_COM_COMQ_FTQ_CTL_INTERVENE                         (1L<<0)
+#define BNX2_COM_COMQ_FTQ_CTL_OVERFLOW                  (1L<<1)
+#define BNX2_COM_COMQ_FTQ_CTL_FORCE_INTERVENE           (1L<<2)
+#define BNX2_COM_COMQ_FTQ_CTL_MAX_DEPTH                         (0x3ffL<<12)
+#define BNX2_COM_COMQ_FTQ_CTL_CUR_DEPTH                         (0x3ffL<<22)
+
+#define BNX2_COM_SCRATCH                               0x00120000
+
+
+/*
+ *  cp_reg definition
+ *  offset: 0x180000
+ */
+#define BNX2_CP_CPU_MODE                               0x00185000
+#define BNX2_CP_CPU_MODE_LOCAL_RST                      (1L<<0)
+#define BNX2_CP_CPU_MODE_STEP_ENA                       (1L<<1)
+#define BNX2_CP_CPU_MODE_PAGE_0_DATA_ENA                (1L<<2)
+#define BNX2_CP_CPU_MODE_PAGE_0_INST_ENA                (1L<<3)
+#define BNX2_CP_CPU_MODE_MSG_BIT1                       (1L<<6)
+#define BNX2_CP_CPU_MODE_INTERRUPT_ENA                  (1L<<7)
+#define BNX2_CP_CPU_MODE_SOFT_HALT                      (1L<<10)
+#define BNX2_CP_CPU_MODE_BAD_DATA_HALT_ENA              (1L<<11)
+#define BNX2_CP_CPU_MODE_BAD_INST_HALT_ENA              (1L<<12)
+#define BNX2_CP_CPU_MODE_FIO_ABORT_HALT_ENA             (1L<<13)
+#define BNX2_CP_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA        (1L<<15)
+
+#define BNX2_CP_CPU_STATE                              0x00185004
+#define BNX2_CP_CPU_STATE_BREAKPOINT                    (1L<<0)
+#define BNX2_CP_CPU_STATE_BAD_INST_HALTED               (1L<<2)
+#define BNX2_CP_CPU_STATE_PAGE_0_DATA_HALTED            (1L<<3)
+#define BNX2_CP_CPU_STATE_PAGE_0_INST_HALTED            (1L<<4)
+#define BNX2_CP_CPU_STATE_BAD_DATA_ADDR_HALTED          (1L<<5)
+#define BNX2_CP_CPU_STATE_BAD_pc_HALTED                         (1L<<6)
+#define BNX2_CP_CPU_STATE_ALIGN_HALTED                  (1L<<7)
+#define BNX2_CP_CPU_STATE_FIO_ABORT_HALTED              (1L<<8)
+#define BNX2_CP_CPU_STATE_SOFT_HALTED                   (1L<<10)
+#define BNX2_CP_CPU_STATE_SPAD_UNDERFLOW                (1L<<11)
+#define BNX2_CP_CPU_STATE_INTERRRUPT                    (1L<<12)
+#define BNX2_CP_CPU_STATE_DATA_ACCESS_STALL             (1L<<14)
+#define BNX2_CP_CPU_STATE_INST_FETCH_STALL              (1L<<15)
+#define BNX2_CP_CPU_STATE_BLOCKED_READ                  (1L<<31)
+
+#define BNX2_CP_CPU_EVENT_MASK                         0x00185008
+#define BNX2_CP_CPU_EVENT_MASK_BREAKPOINT_MASK          (1L<<0)
+#define BNX2_CP_CPU_EVENT_MASK_BAD_INST_HALTED_MASK     (1L<<2)
+#define BNX2_CP_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK  (1L<<3)
+#define BNX2_CP_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK  (1L<<4)
+#define BNX2_CP_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK        (1L<<5)
+#define BNX2_CP_CPU_EVENT_MASK_BAD_PC_HALTED_MASK       (1L<<6)
+#define BNX2_CP_CPU_EVENT_MASK_ALIGN_HALTED_MASK        (1L<<7)
+#define BNX2_CP_CPU_EVENT_MASK_FIO_ABORT_MASK           (1L<<8)
+#define BNX2_CP_CPU_EVENT_MASK_SOFT_HALTED_MASK                 (1L<<10)
+#define BNX2_CP_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK      (1L<<11)
+#define BNX2_CP_CPU_EVENT_MASK_INTERRUPT_MASK           (1L<<12)
+
+#define BNX2_CP_CPU_PROGRAM_COUNTER                    0x0018501c
+#define BNX2_CP_CPU_INSTRUCTION                                0x00185020
+#define BNX2_CP_CPU_DATA_ACCESS                                0x00185024
+#define BNX2_CP_CPU_INTERRUPT_ENABLE                   0x00185028
+#define BNX2_CP_CPU_INTERRUPT_VECTOR                   0x0018502c
+#define BNX2_CP_CPU_INTERRUPT_SAVED_PC                 0x00185030
+#define BNX2_CP_CPU_HW_BREAKPOINT                      0x00185034
+#define BNX2_CP_CPU_HW_BREAKPOINT_DISABLE               (1L<<0)
+#define BNX2_CP_CPU_HW_BREAKPOINT_ADDRESS               (0x3fffffffL<<2)
+
+#define BNX2_CP_CPU_DEBUG_VECT_PEEK                    0x00185038
+#define BNX2_CP_CPU_DEBUG_VECT_PEEK_1_VALUE             (0x7ffL<<0)
+#define BNX2_CP_CPU_DEBUG_VECT_PEEK_1_PEEK_EN           (1L<<11)
+#define BNX2_CP_CPU_DEBUG_VECT_PEEK_1_SEL               (0xfL<<12)
+#define BNX2_CP_CPU_DEBUG_VECT_PEEK_2_VALUE             (0x7ffL<<16)
+#define BNX2_CP_CPU_DEBUG_VECT_PEEK_2_PEEK_EN           (1L<<27)
+#define BNX2_CP_CPU_DEBUG_VECT_PEEK_2_SEL               (0xfL<<28)
+
+#define BNX2_CP_CPU_LAST_BRANCH_ADDR                   0x00185048
+#define BNX2_CP_CPU_LAST_BRANCH_ADDR_TYPE               (1L<<1)
+#define BNX2_CP_CPU_LAST_BRANCH_ADDR_TYPE_JUMP          (0L<<1)
+#define BNX2_CP_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH        (1L<<1)
+#define BNX2_CP_CPU_LAST_BRANCH_ADDR_LBA                (0x3fffffffL<<2)
+
+#define BNX2_CP_CPU_REG_FILE                           0x00185200
+#define BNX2_CP_CPQ_FTQ_DATA                           0x001853c0
+#define BNX2_CP_CPQ_FTQ_CMD                            0x001853f8
+#define BNX2_CP_CPQ_FTQ_CMD_OFFSET                      (0x3ffL<<0)
+#define BNX2_CP_CPQ_FTQ_CMD_WR_TOP                      (1L<<10)
+#define BNX2_CP_CPQ_FTQ_CMD_WR_TOP_0                    (0L<<10)
+#define BNX2_CP_CPQ_FTQ_CMD_WR_TOP_1                    (1L<<10)
+#define BNX2_CP_CPQ_FTQ_CMD_SFT_RESET                   (1L<<25)
+#define BNX2_CP_CPQ_FTQ_CMD_RD_DATA                     (1L<<26)
+#define BNX2_CP_CPQ_FTQ_CMD_ADD_INTERVEN                (1L<<27)
+#define BNX2_CP_CPQ_FTQ_CMD_ADD_DATA                    (1L<<28)
+#define BNX2_CP_CPQ_FTQ_CMD_INTERVENE_CLR               (1L<<29)
+#define BNX2_CP_CPQ_FTQ_CMD_POP                                 (1L<<30)
+#define BNX2_CP_CPQ_FTQ_CMD_BUSY                        (1L<<31)
+
+#define BNX2_CP_CPQ_FTQ_CTL                            0x001853fc
+#define BNX2_CP_CPQ_FTQ_CTL_INTERVENE                   (1L<<0)
+#define BNX2_CP_CPQ_FTQ_CTL_OVERFLOW                    (1L<<1)
+#define BNX2_CP_CPQ_FTQ_CTL_FORCE_INTERVENE             (1L<<2)
+#define BNX2_CP_CPQ_FTQ_CTL_MAX_DEPTH                   (0x3ffL<<12)
+#define BNX2_CP_CPQ_FTQ_CTL_CUR_DEPTH                   (0x3ffL<<22)
+
+#define BNX2_CP_SCRATCH                                        0x001a0000
+
+
+/*
+ *  mcp_reg definition
+ *  offset: 0x140000
+ */
+#define BNX2_MCP_CPU_MODE                              0x00145000
+#define BNX2_MCP_CPU_MODE_LOCAL_RST                     (1L<<0)
+#define BNX2_MCP_CPU_MODE_STEP_ENA                      (1L<<1)
+#define BNX2_MCP_CPU_MODE_PAGE_0_DATA_ENA               (1L<<2)
+#define BNX2_MCP_CPU_MODE_PAGE_0_INST_ENA               (1L<<3)
+#define BNX2_MCP_CPU_MODE_MSG_BIT1                      (1L<<6)
+#define BNX2_MCP_CPU_MODE_INTERRUPT_ENA                         (1L<<7)
+#define BNX2_MCP_CPU_MODE_SOFT_HALT                     (1L<<10)
+#define BNX2_MCP_CPU_MODE_BAD_DATA_HALT_ENA             (1L<<11)
+#define BNX2_MCP_CPU_MODE_BAD_INST_HALT_ENA             (1L<<12)
+#define BNX2_MCP_CPU_MODE_FIO_ABORT_HALT_ENA            (1L<<13)
+#define BNX2_MCP_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA       (1L<<15)
+
+#define BNX2_MCP_CPU_STATE                             0x00145004
+#define BNX2_MCP_CPU_STATE_BREAKPOINT                   (1L<<0)
+#define BNX2_MCP_CPU_STATE_BAD_INST_HALTED              (1L<<2)
+#define BNX2_MCP_CPU_STATE_PAGE_0_DATA_HALTED           (1L<<3)
+#define BNX2_MCP_CPU_STATE_PAGE_0_INST_HALTED           (1L<<4)
+#define BNX2_MCP_CPU_STATE_BAD_DATA_ADDR_HALTED                 (1L<<5)
+#define BNX2_MCP_CPU_STATE_BAD_pc_HALTED                (1L<<6)
+#define BNX2_MCP_CPU_STATE_ALIGN_HALTED                         (1L<<7)
+#define BNX2_MCP_CPU_STATE_FIO_ABORT_HALTED             (1L<<8)
+#define BNX2_MCP_CPU_STATE_SOFT_HALTED                  (1L<<10)
+#define BNX2_MCP_CPU_STATE_SPAD_UNDERFLOW               (1L<<11)
+#define BNX2_MCP_CPU_STATE_INTERRRUPT                   (1L<<12)
+#define BNX2_MCP_CPU_STATE_DATA_ACCESS_STALL            (1L<<14)
+#define BNX2_MCP_CPU_STATE_INST_FETCH_STALL             (1L<<15)
+#define BNX2_MCP_CPU_STATE_BLOCKED_READ                         (1L<<31)
+
+#define BNX2_MCP_CPU_EVENT_MASK                                0x00145008
+#define BNX2_MCP_CPU_EVENT_MASK_BREAKPOINT_MASK                 (1L<<0)
+#define BNX2_MCP_CPU_EVENT_MASK_BAD_INST_HALTED_MASK    (1L<<2)
+#define BNX2_MCP_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK         (1L<<3)
+#define BNX2_MCP_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK         (1L<<4)
+#define BNX2_MCP_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK       (1L<<5)
+#define BNX2_MCP_CPU_EVENT_MASK_BAD_PC_HALTED_MASK      (1L<<6)
+#define BNX2_MCP_CPU_EVENT_MASK_ALIGN_HALTED_MASK       (1L<<7)
+#define BNX2_MCP_CPU_EVENT_MASK_FIO_ABORT_MASK          (1L<<8)
+#define BNX2_MCP_CPU_EVENT_MASK_SOFT_HALTED_MASK        (1L<<10)
+#define BNX2_MCP_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK     (1L<<11)
+#define BNX2_MCP_CPU_EVENT_MASK_INTERRUPT_MASK          (1L<<12)
+
+#define BNX2_MCP_CPU_PROGRAM_COUNTER                   0x0014501c
+#define BNX2_MCP_CPU_INSTRUCTION                       0x00145020
+#define BNX2_MCP_CPU_DATA_ACCESS                       0x00145024
+#define BNX2_MCP_CPU_INTERRUPT_ENABLE                  0x00145028
+#define BNX2_MCP_CPU_INTERRUPT_VECTOR                  0x0014502c
+#define BNX2_MCP_CPU_INTERRUPT_SAVED_PC                        0x00145030
+#define BNX2_MCP_CPU_HW_BREAKPOINT                     0x00145034
+#define BNX2_MCP_CPU_HW_BREAKPOINT_DISABLE              (1L<<0)
+#define BNX2_MCP_CPU_HW_BREAKPOINT_ADDRESS              (0x3fffffffL<<2)
+
+#define BNX2_MCP_CPU_DEBUG_VECT_PEEK                   0x00145038
+#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_1_VALUE            (0x7ffL<<0)
+#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_1_PEEK_EN          (1L<<11)
+#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_1_SEL              (0xfL<<12)
+#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_2_VALUE            (0x7ffL<<16)
+#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_2_PEEK_EN          (1L<<27)
+#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_2_SEL              (0xfL<<28)
+
+#define BNX2_MCP_CPU_LAST_BRANCH_ADDR                  0x00145048
+#define BNX2_MCP_CPU_LAST_BRANCH_ADDR_TYPE              (1L<<1)
+#define BNX2_MCP_CPU_LAST_BRANCH_ADDR_TYPE_JUMP                 (0L<<1)
+#define BNX2_MCP_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH       (1L<<1)
+#define BNX2_MCP_CPU_LAST_BRANCH_ADDR_LBA               (0x3fffffffL<<2)
+
+#define BNX2_MCP_CPU_REG_FILE                          0x00145200
+#define BNX2_MCP_MCPQ_FTQ_DATA                         0x001453c0
+#define BNX2_MCP_MCPQ_FTQ_CMD                          0x001453f8
+#define BNX2_MCP_MCPQ_FTQ_CMD_OFFSET                    (0x3ffL<<0)
+#define BNX2_MCP_MCPQ_FTQ_CMD_WR_TOP                    (1L<<10)
+#define BNX2_MCP_MCPQ_FTQ_CMD_WR_TOP_0                  (0L<<10)
+#define BNX2_MCP_MCPQ_FTQ_CMD_WR_TOP_1                  (1L<<10)
+#define BNX2_MCP_MCPQ_FTQ_CMD_SFT_RESET                         (1L<<25)
+#define BNX2_MCP_MCPQ_FTQ_CMD_RD_DATA                   (1L<<26)
+#define BNX2_MCP_MCPQ_FTQ_CMD_ADD_INTERVEN              (1L<<27)
+#define BNX2_MCP_MCPQ_FTQ_CMD_ADD_DATA                  (1L<<28)
+#define BNX2_MCP_MCPQ_FTQ_CMD_INTERVENE_CLR             (1L<<29)
+#define BNX2_MCP_MCPQ_FTQ_CMD_POP                       (1L<<30)
+#define BNX2_MCP_MCPQ_FTQ_CMD_BUSY                      (1L<<31)
+
+#define BNX2_MCP_MCPQ_FTQ_CTL                          0x001453fc
+#define BNX2_MCP_MCPQ_FTQ_CTL_INTERVENE                         (1L<<0)
+#define BNX2_MCP_MCPQ_FTQ_CTL_OVERFLOW                  (1L<<1)
+#define BNX2_MCP_MCPQ_FTQ_CTL_FORCE_INTERVENE           (1L<<2)
+#define BNX2_MCP_MCPQ_FTQ_CTL_MAX_DEPTH                         (0x3ffL<<12)
+#define BNX2_MCP_MCPQ_FTQ_CTL_CUR_DEPTH                         (0x3ffL<<22)
+
+#define BNX2_MCP_ROM                                   0x00150000
+#define BNX2_MCP_SCRATCH                               0x00160000
+
+
+#define NUM_MC_HASH_REGISTERS   8
+
+
+/* PHY_ID1: bits 31-16; PHY_ID2: bits 15-0.  */
+#define PHY_BCM5706_PHY_ID                          0x00206160
+
+#define PHY_ID(id)                                  ((id) & 0xfffffff0)
+#define PHY_REV_ID(id)                              ((id) & 0xf)
+
+#define MIN_ETHERNET_PACKET_SIZE       60
+#define MAX_ETHERNET_PACKET_SIZE       1514
+#define MAX_ETHERNET_JUMBO_PACKET_SIZE 9014
+
+#define RX_COPY_THRESH                 92
+
+#define DMA_READ_CHANS 5
+#define DMA_WRITE_CHANS        3
+
+#define BCM_PAGE_BITS  12
+#define BCM_PAGE_SIZE  (1 << BCM_PAGE_BITS)
+
+#define TX_DESC_CNT  (BCM_PAGE_SIZE / sizeof(struct tx_bd))
+#define MAX_TX_DESC_CNT (TX_DESC_CNT - 1)
+
+#define RX_DESC_CNT  (BCM_PAGE_SIZE / sizeof(struct rx_bd))
+#define MAX_RX_DESC_CNT (RX_DESC_CNT - 1)
+
+#define NEXT_TX_BD(x) (((x) & (MAX_TX_DESC_CNT - 1)) ==                        \
+               (MAX_TX_DESC_CNT - 1)) ?                                \
+       (x) + 2 : (x) + 1
+
+#define TX_RING_IDX(x) ((x) & MAX_TX_DESC_CNT)
+
+#define NEXT_RX_BD(x) (((x) & (MAX_RX_DESC_CNT - 1)) ==                        \
+               (MAX_RX_DESC_CNT - 1)) ?                                \
+       (x) + 2 : (x) + 1
+
+#define RX_RING_IDX(x) ((x) & MAX_RX_DESC_CNT)
+
+
+/* Context size. */
+#define CTX_SHIFT                   7
+#define CTX_SIZE                    (1 << CTX_SHIFT)
+#define CTX_MASK                    (CTX_SIZE - 1)
+#define GET_CID_ADDR(_cid)          ((_cid) << CTX_SHIFT)
+#define GET_CID(_cid_addr)          ((_cid_addr) >> CTX_SHIFT)
+
+#define PHY_CTX_SHIFT               6
+#define PHY_CTX_SIZE                (1 << PHY_CTX_SHIFT)
+#define PHY_CTX_MASK                (PHY_CTX_SIZE - 1)
+#define GET_PCID_ADDR(_pcid)        ((_pcid) << PHY_CTX_SHIFT)
+#define GET_PCID(_pcid_addr)        ((_pcid_addr) >> PHY_CTX_SHIFT)
+
+#define MB_KERNEL_CTX_SHIFT         8
+#define MB_KERNEL_CTX_SIZE          (1 << MB_KERNEL_CTX_SHIFT)
+#define MB_KERNEL_CTX_MASK          (MB_KERNEL_CTX_SIZE - 1)
+#define MB_GET_CID_ADDR(_cid)       (0x10000 + ((_cid) << MB_KERNEL_CTX_SHIFT))
+
+#define MAX_CID_CNT                 0x4000
+#define MAX_CID_ADDR                (GET_CID_ADDR(MAX_CID_CNT))
+#define INVALID_CID_ADDR            0xffffffff
+
+#define TX_CID         16
+#define RX_CID         0
+
+#define MB_TX_CID_ADDR MB_GET_CID_ADDR(TX_CID)
+#define MB_RX_CID_ADDR MB_GET_CID_ADDR(RX_CID)
+
+struct sw_bd {
+       struct sk_buff          *skb;
+       DECLARE_PCI_UNMAP_ADDR(mapping)
+};
+
+/* Buffered flash (Atmel: AT45DB011B) specific information */
+#define SEEPROM_PAGE_BITS                      2
+#define SEEPROM_PHY_PAGE_SIZE                  (1 << SEEPROM_PAGE_BITS)
+#define SEEPROM_BYTE_ADDR_MASK                 (SEEPROM_PHY_PAGE_SIZE-1)
+#define SEEPROM_PAGE_SIZE                      4
+#define SEEPROM_TOTAL_SIZE                     65536
+
+#define BUFFERED_FLASH_PAGE_BITS               9
+#define BUFFERED_FLASH_PHY_PAGE_SIZE           (1 << BUFFERED_FLASH_PAGE_BITS)
+#define BUFFERED_FLASH_BYTE_ADDR_MASK          (BUFFERED_FLASH_PHY_PAGE_SIZE-1)
+#define BUFFERED_FLASH_PAGE_SIZE               264
+#define BUFFERED_FLASH_TOTAL_SIZE              131072
+
+#define SAIFUN_FLASH_PAGE_BITS                 8
+#define SAIFUN_FLASH_PHY_PAGE_SIZE             (1 << SAIFUN_FLASH_PAGE_BITS)
+#define SAIFUN_FLASH_BYTE_ADDR_MASK            (SAIFUN_FLASH_PHY_PAGE_SIZE-1)
+#define SAIFUN_FLASH_PAGE_SIZE                 256
+#define SAIFUN_FLASH_BASE_TOTAL_SIZE           65536
+
+#define NVRAM_TIMEOUT_COUNT                    30000
+
+
+#define FLASH_STRAP_MASK                       (BNX2_NVM_CFG1_FLASH_MODE   | \
+                                                BNX2_NVM_CFG1_BUFFER_MODE  | \
+                                                BNX2_NVM_CFG1_PROTECT_MODE | \
+                                                BNX2_NVM_CFG1_FLASH_SIZE)
+
+struct flash_spec {
+       u32 strapping;
+       u32 config1;
+       u32 config2;
+       u32 config3;
+       u32 write1;
+       u32 buffered;
+       u32 page_bits;
+       u32 page_size;
+       u32 addr_mask;
+       u32 total_size;
+       u8  *name;
+};
+
+struct bnx2 {
+       /* Fields used in the tx and intr/napi performance paths are grouped */
+       /* together in the beginning of the structure. */
+       void __iomem            *regview;
+
+       struct net_device       *dev;
+       struct pci_dev          *pdev;
+
+       atomic_t                intr_sem;
+
+       struct status_block     *status_blk;
+       u32                     last_status_idx;
+
+       atomic_t                tx_avail_bd;
+       struct tx_bd            *tx_desc_ring;
+       struct sw_bd            *tx_buf_ring;
+       u32                     tx_prod_bseq;
+       u16                     tx_prod;
+       u16                     tx_cons;
+
+#ifdef BCM_VLAN 
+       struct                  vlan_group *vlgrp;
+#endif
+
+       u32                     rx_offset;
+       u32                     rx_buf_use_size;        /* useable size */
+       u32                     rx_buf_size;            /* with alignment */
+       struct rx_bd            *rx_desc_ring;
+       struct sw_bd            *rx_buf_ring;
+       u32                     rx_prod_bseq;
+       u16                     rx_prod;
+       u16                     rx_cons;
+
+       u32                     rx_csum;
+
+       /* Only used to synchronize netif_stop_queue/wake_queue when tx */
+       /* ring is full */
+       spinlock_t              tx_lock;
+
+       /* End of fileds used in the performance code paths. */
+
+       char                    *name;
+
+       int                     timer_interval;
+       struct                  timer_list timer;
+       struct work_struct      reset_task;
+
+       /* Used to synchronize phy accesses. */
+       spinlock_t              phy_lock;
+
+       u32                     flags;
+#define PCIX_FLAG                      1
+#define PCI_32BIT_FLAG                 2
+#define ONE_TDMA_FLAG                  4       /* no longer used */
+#define NO_WOL_FLAG                    8
+#define USING_DAC_FLAG                 0x10
+#define USING_MSI_FLAG                 0x20
+
+       u32                     phy_flags;
+#define PHY_SERDES_FLAG                        1
+#define PHY_CRC_FIX_FLAG               2
+#define PHY_PARALLEL_DETECT_FLAG       4
+#define PHY_INT_MODE_MASK_FLAG         0x300
+#define PHY_INT_MODE_AUTO_POLLING_FLAG 0x100
+#define PHY_INT_MODE_LINK_READY_FLAG   0x200
+
+       u32                     chip_id;
+       /* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */
+#define CHIP_NUM(bp)                   (((bp)->chip_id) & 0xffff0000)
+#define CHIP_NUM_5706                  0x57060000
+
+#define CHIP_REV(bp)                   (((bp)->chip_id) & 0x0000f000)
+#define CHIP_REV_Ax                    0x00000000
+#define CHIP_REV_Bx                    0x00001000
+#define CHIP_REV_Cx                    0x00002000
+    
+#define CHIP_METAL(bp)                 (((bp)->chip_id) & 0x00000ff0)
+#define CHIP_BONDING(bp)               (((bp)->chip_id) & 0x0000000f)
+
+#define CHIP_ID(bp)                    (((bp)->chip_id) & 0xfffffff0)
+#define CHIP_ID_5706_A0                        0x57060000
+#define CHIP_ID_5706_A1                        0x57060010
+
+#define CHIP_BOND_ID(bp)               (((bp)->chip_id) & 0xf)
+
+/* A serdes chip will have the first bit of the bond id set. */
+#define CHIP_BOND_ID_SERDES_BIT                0x01
+
+       u32                     phy_addr;
+       u32                     phy_id;
+       
+       u16                     bus_speed_mhz;
+       u8                      wol;
+
+       u8                      fw_timed_out;
+
+       u16                     fw_wr_seq;
+       u16                     fw_drv_pulse_wr_seq;
+
+       int                     tx_ring_size;
+       dma_addr_t              tx_desc_mapping;
+
+
+       int                     rx_ring_size;
+       dma_addr_t              rx_desc_mapping;
+
+       u16                     tx_quick_cons_trip;
+       u16                     tx_quick_cons_trip_int;
+       u16                     rx_quick_cons_trip;
+       u16                     rx_quick_cons_trip_int;
+       u16                     comp_prod_trip;
+       u16                     comp_prod_trip_int;
+       u16                     tx_ticks;
+       u16                     tx_ticks_int;
+       u16                     com_ticks;
+       u16                     com_ticks_int;
+       u16                     cmd_ticks;
+       u16                     cmd_ticks_int;
+       u16                     rx_ticks;
+       u16                     rx_ticks_int;
+
+       u32                     stats_ticks;
+
+       dma_addr_t              status_blk_mapping;
+
+       struct statistics_block *stats_blk;
+       dma_addr_t              stats_blk_mapping;
+
+       u32                     rx_mode;
+
+       u16                     req_line_speed;
+       u8                      req_duplex;
+
+       u8                      link_up;
+
+       u16                     line_speed;
+       u8                      duplex;
+       u8                      flow_ctrl;      /* actual flow ctrl settings */
+                                               /* may be different from     */
+                                               /* req_flow_ctrl if autoneg  */
+#define FLOW_CTRL_TX           1
+#define FLOW_CTRL_RX           2
+
+       u32                     advertising;
+
+       u8                      req_flow_ctrl;  /* flow ctrl advertisement */ 
+                                               /* settings or forced      */
+                                               /* settings                */
+       u8                      autoneg;
+#define AUTONEG_SPEED          1
+#define AUTONEG_FLOW_CTRL      2
+
+       u8                      loopback;
+#define MAC_LOOPBACK           1
+#define PHY_LOOPBACK           2
+
+       u8                      serdes_an_pending;
+#define SERDES_AN_TIMEOUT      (2 * HZ)
+
+       u8                      mac_addr[8];
+
+       u32                     fw_ver;
+
+       int                     pm_cap;
+       int                     pcix_cap;
+
+       struct net_device_stats net_stats;
+
+       struct flash_spec       *flash_info;
+};
+
+static u32 bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset);
+static void bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val);
+
+#define REG_RD(bp, offset)                                     \
+       readl(bp->regview + offset)
+
+#define REG_WR(bp, offset, val)                                        \
+       writel(val, bp->regview + offset)
+
+#define REG_WR16(bp, offset, val)                              \
+       writew(val, bp->regview + offset)
+
+#define REG_RD_IND(bp, offset)                                 \
+       bnx2_reg_rd_ind(bp, offset)
+
+#define REG_WR_IND(bp, offset, val)                            \
+       bnx2_reg_wr_ind(bp, offset, val)
+
+/* Indirect context access.  Unlike the MBQ_WR, these macros will not
+ * trigger a chip event. */
+static void bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val);
+
+#define CTX_WR(bp, cid_addr, offset, val)                      \
+       bnx2_ctx_wr(bp, cid_addr, offset, val)
+
+struct cpu_reg {
+       u32 mode;
+       u32 mode_value_halt;
+       u32 mode_value_sstep;
+
+       u32 state;
+       u32 state_value_clear;
+
+       u32 gpr0;
+       u32 evmask;
+       u32 pc;
+       u32 inst;
+       u32 bp;
+
+       u32 spad_base;
+
+       u32 mips_view_base;
+};
+
+struct fw_info {
+       u32 ver_major;
+       u32 ver_minor;
+       u32 ver_fix;
+
+       u32 start_addr;
+
+       /* Text section. */
+       u32 text_addr;
+       u32 text_len;
+       u32 text_index;
+       u32 *text;
+
+       /* Data section. */
+       u32 data_addr;
+       u32 data_len;
+       u32 data_index;
+       u32 *data;
+
+       /* SBSS section. */
+       u32 sbss_addr;
+       u32 sbss_len;
+       u32 sbss_index;
+       u32 *sbss;
+
+       /* BSS section. */
+       u32 bss_addr;
+       u32 bss_len;
+       u32 bss_index;
+       u32 *bss;
+
+       /* Read-only section. */
+       u32 rodata_addr;
+       u32 rodata_len;
+       u32 rodata_index;
+       u32 *rodata;
+};
+
+#define RV2P_PROC1                              0
+#define RV2P_PROC2                              1
+
+
+/* This value (in milliseconds) determines the frequency of the driver
+ * issuing the PULSE message code.  The firmware monitors this periodic
+ * pulse to determine when to switch to an OS-absent mode. */
+#define DRV_PULSE_PERIOD_MS                 250
+
+/* This value (in milliseconds) determines how long the driver should
+ * wait for an acknowledgement from the firmware before timing out.  Once
+ * the firmware has timed out, the driver will assume there is no firmware
+ * running and there won't be any firmware-driver synchronization during a
+ * driver reset. */
+#define FW_ACK_TIME_OUT_MS                  50
+
+
+#define BNX2_DRV_RESET_SIGNATURE               0x00000000
+#define BNX2_DRV_RESET_SIGNATURE_MAGIC          0x4841564b /* HAVK */
+//#define DRV_RESET_SIGNATURE_MAGIC             0x47495352 /* RSIG */
+
+#define BNX2_DRV_MB                            0x00000004
+#define BNX2_DRV_MSG_CODE                       0xff000000
+#define BNX2_DRV_MSG_CODE_RESET                         0x01000000
+#define BNX2_DRV_MSG_CODE_UNLOAD                0x02000000
+#define BNX2_DRV_MSG_CODE_SHUTDOWN              0x03000000
+#define BNX2_DRV_MSG_CODE_SUSPEND_WOL           0x04000000
+#define BNX2_DRV_MSG_CODE_FW_TIMEOUT            0x05000000
+#define BNX2_DRV_MSG_CODE_PULSE                         0x06000000
+#define BNX2_DRV_MSG_CODE_DIAG                  0x07000000
+#define BNX2_DRV_MSG_CODE_SUSPEND_NO_WOL        0x09000000
+
+#define BNX2_DRV_MSG_DATA                       0x00ff0000
+#define BNX2_DRV_MSG_DATA_WAIT0                         0x00010000
+#define BNX2_DRV_MSG_DATA_WAIT1                         0x00020000
+#define BNX2_DRV_MSG_DATA_WAIT2                         0x00030000
+#define BNX2_DRV_MSG_DATA_WAIT3                         0x00040000
+        
+#define BNX2_DRV_MSG_SEQ                        0x0000ffff
+
+#define BNX2_FW_MB                             0x00000008
+#define BNX2_FW_MSG_ACK                                 0x0000ffff
+#define BNX2_FW_MSG_STATUS_MASK                         0x00ff0000
+#define BNX2_FW_MSG_STATUS_OK                   0x00000000
+#define BNX2_FW_MSG_STATUS_FAILURE              0x00ff0000
+
+#define BNX2_LINK_STATUS                       0x0000000c
+
+#define BNX2_DRV_PULSE_MB                      0x00000010
+#define BNX2_DRV_PULSE_SEQ_MASK                         0x0000ffff
+
+/* Indicate to the firmware not to go into the
+ * OS absent when it is not getting driver pulse.
+ * This is used for debugging. */
+#define BNX2_DRV_MSG_DATA_PULSE_CODE_ALWAYS_ALIVE       0x00010000
+
+#define BNX2_DEV_INFO_SIGNATURE                        0x00000020
+#define BNX2_DEV_INFO_SIGNATURE_MAGIC           0x44564900
+#define BNX2_DEV_INFO_SIGNATURE_MAGIC_MASK      0xffffff00
+#define BNX2_DEV_INFO_FEATURE_CFG_VALID                 0x01
+#define BNX2_DEV_INFO_SECONDARY_PORT            0x80
+#define BNX2_DEV_INFO_DRV_ALWAYS_ALIVE          0x40
+
+#define BNX2_SHARED_HW_CFG_PART_NUM            0x00000024
+
+#define BNX2_SHARED_HW_CFG_POWER_DISSIPATED    0x00000034
+#define BNX2_SHARED_HW_CFG_POWER_STATE_D3_MASK  0xff000000
+#define BNX2_SHARED_HW_CFG_POWER_STATE_D2_MASK  0xff0000
+#define BNX2_SHARED_HW_CFG_POWER_STATE_D1_MASK  0xff00
+#define BNX2_SHARED_HW_CFG_POWER_STATE_D0_MASK  0xff
+
+#define BNX2_SHARED_HW_CFG POWER_CONSUMED      0x00000038
+#define BNX2_SHARED_HW_CFG_CONFIG              0x0000003c
+#define BNX2_SHARED_HW_CFG_DESIGN_NIC           0
+#define BNX2_SHARED_HW_CFG_DESIGN_LOM           0x1
+#define BNX2_SHARED_HW_CFG_PHY_COPPER           0
+#define BNX2_SHARED_HW_CFG_PHY_FIBER            0x2
+#define BNX2_SHARED_HW_CFG_LED_MODE_SHIFT_BITS  8
+#define BNX2_SHARED_HW_CFG_LED_MODE_MASK        0x300
+#define BNX2_SHARED_HW_CFG_LED_MODE_MAC                 0
+#define BNX2_SHARED_HW_CFG_LED_MODE_GPHY1       0x100
+#define BNX2_SHARED_HW_CFG_LED_MODE_GPHY2       0x200
+
+#define BNX2_DEV_INFO_BC_REV                   0x0000004c
+
+#define BNX2_PORT_HW_CFG_MAC_UPPER             0x00000050
+#define BNX2_PORT_HW_CFG_UPPERMAC_MASK          0xffff
+
+#define BNX2_PORT_HW_CFG_MAC_LOWER             0x00000054
+#define BNX2_PORT_HW_CFG_CONFIG                        0x00000058
+
+#define BNX2_PORT_HW_CFG_IMD_MAC_A_UPPER       0x00000068
+#define BNX2_PORT_HW_CFG_IMD_MAC_A_LOWER       0x0000006c
+#define BNX2_PORT_HW_CFG_IMD_MAC_B_UPPER       0x00000070
+#define BNX2_PORT_HW_CFG_IMD_MAC_B_LOWER       0x00000074
+#define BNX2_PORT_HW_CFG_ISCSI_MAC_UPPER       0x00000078
+#define BNX2_PORT_HW_CFG_ISCSI_MAC_LOWER       0x0000007c
+
+#define BNX2_DEV_INFO_PER_PORT_HW_CONFIG2      0x000000b4
+
+#define BNX2_DEV_INFO_FORMAT_REV               0x000000c4
+#define BNX2_DEV_INFO_FORMAT_REV_MASK           0xff000000
+#define BNX2_DEV_INFO_FORMAT_REV_ID             ('A' << 24)
+
+#define BNX2_SHARED_FEATURE                    0x000000c8
+#define BNX2_SHARED_FEATURE_MASK                0xffffffff
+
+#define BNX2_PORT_FEATURE                      0x000000d8
+#define BNX2_PORT2_FEATURE                     0x00000014c
+#define BNX2_PORT_FEATURE_WOL_ENABLED           0x01000000
+#define BNX2_PORT_FEATURE_MBA_ENABLED           0x02000000
+#define BNX2_PORT_FEATURE_ASF_ENABLED           0x04000000
+#define BNX2_PORT_FEATURE_IMD_ENABLED           0x08000000
+#define BNX2_PORT_FEATURE_BAR1_SIZE_MASK        0xf
+#define BNX2_PORT_FEATURE_BAR1_SIZE_DISABLED    0x0
+#define BNX2_PORT_FEATURE_BAR1_SIZE_64K                 0x1
+#define BNX2_PORT_FEATURE_BAR1_SIZE_128K        0x2
+#define BNX2_PORT_FEATURE_BAR1_SIZE_256K        0x3
+#define BNX2_PORT_FEATURE_BAR1_SIZE_512K        0x4
+#define BNX2_PORT_FEATURE_BAR1_SIZE_1M          0x5
+#define BNX2_PORT_FEATURE_BAR1_SIZE_2M          0x6
+#define BNX2_PORT_FEATURE_BAR1_SIZE_4M          0x7
+#define BNX2_PORT_FEATURE_BAR1_SIZE_8M          0x8
+#define BNX2_PORT_FEATURE_BAR1_SIZE_16M                 0x9
+#define BNX2_PORT_FEATURE_BAR1_SIZE_32M                 0xa
+#define BNX2_PORT_FEATURE_BAR1_SIZE_64M                 0xb
+#define BNX2_PORT_FEATURE_BAR1_SIZE_128M        0xc
+#define BNX2_PORT_FEATURE_BAR1_SIZE_256M        0xd
+#define BNX2_PORT_FEATURE_BAR1_SIZE_512M        0xe
+#define BNX2_PORT_FEATURE_BAR1_SIZE_1G          0xf
+
+#define BNX2_PORT_FEATURE_WOL                  0xdc
+#define BNX2_PORT2_FEATURE_WOL                 0x150
+#define BNX2_PORT_FEATURE_WOL_DEFAULT_SHIFT_BITS        4
+#define BNX2_PORT_FEATURE_WOL_DEFAULT_MASK      0x30
+#define BNX2_PORT_FEATURE_WOL_DEFAULT_DISABLE   0
+#define BNX2_PORT_FEATURE_WOL_DEFAULT_MAGIC     0x10
+#define BNX2_PORT_FEATURE_WOL_DEFAULT_ACPI      0x20
+#define BNX2_PORT_FEATURE_WOL_DEFAULT_MAGIC_AND_ACPI    0x30
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_MASK   0xf
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_AUTONEG        0
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_10HALF         1
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_10FULL         2
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_100HALF 3
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_100FULL 4
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_1000HALF       5
+#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_1000FULL       6
+#define BNX2_PORT_FEATURE_WOL_AUTONEG_ADVERTISE_1000    0x40
+#define BNX2_PORT_FEATURE_WOL_RESERVED_PAUSE_CAP 0x400
+#define BNX2_PORT_FEATURE_WOL_RESERVED_ASYM_PAUSE_CAP   0x800
+
+#define BNX2_PORT_FEATURE_MBA                  0xe0
+#define BNX2_PORT2_FEATURE_MBA                 0x154
+#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_SHIFT_BITS        0
+#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_MASK      0x3
+#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_PXE       0
+#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_RPL       1
+#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_BOOTP     2
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_SHIFT_BITS     2
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_MASK   0x3c
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_AUTONEG        0
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_10HALF         0x4
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_10FULL         0x8
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_100HALF        0xc
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_100FULL        0x10
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_1000HALF       0x14
+#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_1000FULL       0x18
+#define BNX2_PORT_FEATURE_MBA_SETUP_PROMPT_ENABLE       0x40
+#define BNX2_PORT_FEATURE_MBA_HOTKEY_CTRL_S     0
+#define BNX2_PORT_FEATURE_MBA_HOTKEY_CTRL_B     0x80
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_SHIFT_BITS   8
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_MASK         0xff00
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_DISABLED     0
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_1K   0x100
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_2K   0x200
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_4K   0x300
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_8K   0x400
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_16K  0x500
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_32K  0x600
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_64K  0x700
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_128K         0x800
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_256K         0x900
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_512K         0xa00
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_1M   0xb00
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_2M   0xc00
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_4M   0xd00
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_8M   0xe00
+#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_16M  0xf00
+#define BNX2_PORT_FEATURE_MBA_MSG_TIMEOUT_SHIFT_BITS    16
+#define BNX2_PORT_FEATURE_MBA_MSG_TIMEOUT_MASK  0xf0000
+#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_SHIFT_BITS         20
+#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_MASK       0x300000
+#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_AUTO       0
+#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_BBS        0x100000
+#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_INT18H     0x200000
+#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_INT19H     0x300000
+
+#define BNX2_PORT_FEATURE_IMD                  0xe4
+#define BNX2_PORT2_FEATURE_IMD                 0x158
+#define BNX2_PORT_FEATURE_IMD_LINK_OVERRIDE_DEFAULT     0
+#define BNX2_PORT_FEATURE_IMD_LINK_OVERRIDE_ENABLE      1
+
+#define BNX2_PORT_FEATURE_VLAN                 0xe8
+#define BNX2_PORT2_FEATURE_VLAN                        0x15c
+#define BNX2_PORT_FEATURE_MBA_VLAN_TAG_MASK     0xffff
+#define BNX2_PORT_FEATURE_MBA_VLAN_ENABLE       0x10000
+
+#define BNX2_BC_STATE_RESET_TYPE               0x000001c0
+#define BNX2_BC_STATE_RESET_TYPE_SIG            0x00005254
+#define BNX2_BC_STATE_RESET_TYPE_SIG_MASK       0x0000ffff
+#define BNX2_BC_STATE_RESET_TYPE_NONE   (BNX2_BC_STATE_RESET_TYPE_SIG | \
+                                         0x00010000)
+#define BNX2_BC_STATE_RESET_TYPE_PCI    (BNX2_BC_STATE_RESET_TYPE_SIG | \
+                                         0x00020000)
+#define BNX2_BC_STATE_RESET_TYPE_VAUX   (BNX2_BC_STATE_RESET_TYPE_SIG | \
+                                         0x00030000)
+#define BNX2_BC_STATE_RESET_TYPE_DRV_MASK       DRV_MSG_CODE         
+#define BNX2_BC_STATE_RESET_TYPE_DRV_RESET (BNX2_BC_STATE_RESET_TYPE_SIG | \
+                                           DRV_MSG_CODE_RESET)
+#define BNX2_BC_STATE_RESET_TYPE_DRV_UNLOAD (BNX2_BC_STATE_RESET_TYPE_SIG | \
+                                            DRV_MSG_CODE_UNLOAD)
+#define BNX2_BC_STATE_RESET_TYPE_DRV_SHUTDOWN (BNX2_BC_STATE_RESET_TYPE_SIG | \
+                                              DRV_MSG_CODE_SHUTDOWN)
+#define BNX2_BC_STATE_RESET_TYPE_DRV_WOL (BNX2_BC_STATE_RESET_TYPE_SIG | \
+                                         DRV_MSG_CODE_WOL)
+#define BNX2_BC_STATE_RESET_TYPE_DRV_DIAG (BNX2_BC_STATE_RESET_TYPE_SIG | \
+                                          DRV_MSG_CODE_DIAG)
+#define BNX2_BC_STATE_RESET_TYPE_VALUE(msg) (BNX2_BC_STATE_RESET_TYPE_SIG | \
+                                            (msg))
+
+#define BNX2_BC_STATE                          0x000001c4
+#define BNX2_BC_STATE_ERR_MASK                  0x0000ff00
+#define BNX2_BC_STATE_SIGN                      0x42530000
+#define BNX2_BC_STATE_SIGN_MASK                         0xffff0000
+#define BNX2_BC_STATE_BC1_START                         (BNX2_BC_STATE_SIGN | 0x1)
+#define BNX2_BC_STATE_GET_NVM_CFG1              (BNX2_BC_STATE_SIGN | 0x2)
+#define BNX2_BC_STATE_PROG_BAR                  (BNX2_BC_STATE_SIGN | 0x3)
+#define BNX2_BC_STATE_INIT_VID                  (BNX2_BC_STATE_SIGN | 0x4)
+#define BNX2_BC_STATE_GET_NVM_CFG2              (BNX2_BC_STATE_SIGN | 0x5)
+#define BNX2_BC_STATE_APPLY_WKARND              (BNX2_BC_STATE_SIGN | 0x6)
+#define BNX2_BC_STATE_LOAD_BC2                  (BNX2_BC_STATE_SIGN | 0x7)
+#define BNX2_BC_STATE_GOING_BC2                         (BNX2_BC_STATE_SIGN | 0x8)
+#define BNX2_BC_STATE_GOING_DIAG                (BNX2_BC_STATE_SIGN | 0x9)
+#define BNX2_BC_STATE_RT_FINAL_INIT             (BNX2_BC_STATE_SIGN | 0x81)
+#define BNX2_BC_STATE_RT_WKARND                         (BNX2_BC_STATE_SIGN | 0x82)
+#define BNX2_BC_STATE_RT_DRV_PULSE              (BNX2_BC_STATE_SIGN | 0x83)
+#define BNX2_BC_STATE_RT_FIOEVTS                (BNX2_BC_STATE_SIGN | 0x84)
+#define BNX2_BC_STATE_RT_DRV_CMD                (BNX2_BC_STATE_SIGN | 0x85)
+#define BNX2_BC_STATE_RT_LOW_POWER              (BNX2_BC_STATE_SIGN | 0x86)
+#define BNX2_BC_STATE_RT_SET_WOL                (BNX2_BC_STATE_SIGN | 0x87)
+#define BNX2_BC_STATE_RT_OTHER_FW               (BNX2_BC_STATE_SIGN | 0x88)
+#define BNX2_BC_STATE_RT_GOING_D3               (BNX2_BC_STATE_SIGN | 0x89)
+#define BNX2_BC_STATE_ERR_BAD_VERSION           (BNX2_BC_STATE_SIGN | 0x0100)
+#define BNX2_BC_STATE_ERR_BAD_BC2_CRC           (BNX2_BC_STATE_SIGN | 0x0200)
+#define BNX2_BC_STATE_ERR_BC1_LOOP              (BNX2_BC_STATE_SIGN | 0x0300)
+#define BNX2_BC_STATE_ERR_UNKNOWN_CMD           (BNX2_BC_STATE_SIGN | 0x0400)
+#define BNX2_BC_STATE_ERR_DRV_DEAD              (BNX2_BC_STATE_SIGN | 0x0500)
+#define BNX2_BC_STATE_ERR_NO_RXP                (BNX2_BC_STATE_SIGN | 0x0600)
+#define BNX2_BC_STATE_ERR_TOO_MANY_RBUF                 (BNX2_BC_STATE_SIGN | 0x0700)
+       
+#define BNX2_BC_STATE_DEBUG_CMD                        0x1dc
+#define BNX2_BC_STATE_BC_DBG_CMD_SIGNATURE      0x42440000
+#define BNX2_BC_STATE_BC_DBG_CMD_SIGNATURE_MASK         0xffff0000
+#define BNX2_BC_STATE_BC_DBG_CMD_LOOP_CNT_MASK  0xffff
+#define BNX2_BC_STATE_BC_DBG_CMD_LOOP_INFINITE  0xffff
+
+#define HOST_VIEW_SHMEM_BASE                   0x167c00
+
+#endif
diff --git a/drivers/net/bnx2_fw.h b/drivers/net/bnx2_fw.h
new file mode 100644 (file)
index 0000000..35f3a2a
--- /dev/null
@@ -0,0 +1,2468 @@
+/* bnx2_fw.h: Broadcom NX2 network driver.
+ *
+ * Copyright (c) 2004, 2005 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, except as noted below.
+ *
+ * This file contains firmware data derived from proprietary unpublished
+ * source code, Copyright (c) 2004, 2005 Broadcom Corporation.
+ *
+ * Permission is hereby granted for the distribution of this firmware data
+ * in hexadecimal or equivalent format, provided this copyright notice is
+ * accompanying it.
+ */
+
+
+static int bnx2_COM_b06FwReleaseMajor = 0x0;
+static int bnx2_COM_b06FwReleaseMinor = 0x0;
+static int bnx2_COM_b06FwReleaseFix = 0x0;
+static u32 bnx2_COM_b06FwStartAddr = 0x080004a0;
+static u32 bnx2_COM_b06FwTextAddr = 0x08000000;
+static int bnx2_COM_b06FwTextLen = 0x4594;
+static u32 bnx2_COM_b06FwDataAddr = 0x080045e0;
+static int bnx2_COM_b06FwDataLen = 0x0;
+static u32 bnx2_COM_b06FwRodataAddr = 0x08004598;
+static int bnx2_COM_b06FwRodataLen = 0x18;
+static u32 bnx2_COM_b06FwBssAddr = 0x08004600;
+static int bnx2_COM_b06FwBssLen = 0x88;
+static u32 bnx2_COM_b06FwSbssAddr = 0x080045e0;
+static int bnx2_COM_b06FwSbssLen = 0x1c;
+static u32 bnx2_COM_b06FwText[(0x4594/4) + 1] = {
+       0x0a000128, 0x00000000, 0x00000000, 0x0000000d, 0x636f6d20, 0x302e362e,
+       0x39000000, 0x00060902, 0x00000000, 0x00000003, 0x00000014, 0x00000032,
+       0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000010, 0x000003e8, 0x0000ea60, 0x00000001, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x0000ffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000002, 0x00000020, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x10000003, 0x00000000, 0x0000000d,
+       0x0000000d, 0x3c020800, 0x244245e0, 0x3c030800, 0x24634688, 0xac400000,
+       0x0043202b, 0x1480fffd, 0x24420004, 0x3c1d0800, 0x37bd7ffc, 0x03a0f021,
+       0x3c100800, 0x261004a0, 0x3c1c0800, 0x279c45e0, 0x0e0001f2, 0x00000000,
+       0x0000000d, 0x27bdffe8, 0x3c1a8000, 0x3c020008, 0x0342d825, 0x3c036010,
+       0xafbf0010, 0x8c655000, 0x3c020800, 0x24470ac8, 0x3c040800, 0x24864600,
+       0x2402ff7f, 0x00a22824, 0x34a5380c, 0xac655000, 0x00002821, 0x24020037,
+       0x24030c80, 0xaf420008, 0xaf430024, 0xacc70000, 0x24a50001, 0x2ca20016,
+       0x1440fffc, 0x24c60004, 0x24844600, 0x3c020800, 0x24420ad4, 0x3c030800,
+       0x246309d4, 0xac820004, 0x3c020800, 0x24420618, 0x3c050800, 0x24a50ca0,
+       0xac82000c, 0x3c020800, 0x24423100, 0xac830008, 0x3c030800, 0x246325c8,
+       0xac820014, 0x3c020800, 0x24422b0c, 0xac830018, 0xac83001c, 0x3c030800,
+       0x24630adc, 0xac820024, 0x3c020800, 0x24423040, 0xac83002c, 0x3c030800,
+       0x24633060, 0xac820030, 0x3c020800, 0x24422f6c, 0xac830034, 0x3c030800,
+       0x24632c60, 0xac82003c, 0x3c020800, 0x24420b6c, 0xac850010, 0xac850020,
+       0xac830040, 0x0e000bd6, 0xac820050, 0x8fbf0010, 0x03e00008, 0x27bd0018,
+       0x27bdffe0, 0xafb00010, 0x27500100, 0xafbf0018, 0xafb10014, 0x9203000b,
+       0x24020003, 0x1462005b, 0x96110008, 0x32220001, 0x10400009, 0x27430080,
+       0x8e020000, 0x96040014, 0x000211c2, 0x00021040, 0x00621821, 0xa4640000,
+       0x0a0001cb, 0x3c020800, 0x3c020800, 0x8c430020, 0x1060002a, 0x3c030800,
+       0x0e001006, 0x00000000, 0x97420108, 0x8f850018, 0x9743010c, 0x3042003e,
+       0x00021400, 0x00621825, 0xaca30000, 0x8f840018, 0x8f420100, 0xac820004,
+       0x97430116, 0x9742010e, 0x8f840018, 0x00031c00, 0x00431025, 0xac820008,
+       0x97430110, 0x97440112, 0x8f850018, 0x00031c00, 0x00832025, 0xaca4000c,
+       0x97420114, 0x8f840018, 0x3042ffff, 0xac820010, 0x8f830018, 0xac600014,
+       0x8f820018, 0x3c030800, 0xac400018, 0x9462466e, 0x8f840018, 0x3c032000,
+       0x00431025, 0xac82001c, 0x0e001044, 0x24040001, 0x3c030800, 0x8c620040,
+       0x24420001, 0xac620040, 0x3c020800, 0x8c430044, 0x32240004, 0x24630001,
+       0x10800017, 0xac430044, 0x8f4202b8, 0x04430007, 0x8e020020, 0x3c040800,
+       0x8c830060, 0x24020001, 0x24630001, 0x0a0001ed, 0xac830060, 0x3c060800,
+       0x8cc4005c, 0xaf420280, 0x96030016, 0x00001021, 0xa7430284, 0x8e050004,
+       0x24840001, 0x3c031000, 0xaf450288, 0xaf4302b8, 0x0a0001ed, 0xacc4005c,
+       0x32220002, 0x0a0001ed, 0x0002102b, 0x3c026000, 0xac400808, 0x0000000d,
+       0x00001021, 0x8fbf0018, 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020,
+       0x27bdffc8, 0xafbf0034, 0xafbe0030, 0xafb7002c, 0xafb60028, 0xafb50024,
+       0xafb40020, 0xafb3001c, 0xafb20018, 0xafb10014, 0x0e00013f, 0xafb00010,
+       0x24110020, 0x24150030, 0x2794000c, 0x27930008, 0x3c124000, 0x3c1e0800,
+       0x3c170800, 0x3c160800, 0x8f820004, 0x3c040800, 0x8c830020, 0x10430004,
+       0x00000000, 0xaf830004, 0x0e00110b, 0x00000000, 0x8f500000, 0x32020007,
+       0x1040fff5, 0x32020001, 0x1040002b, 0x32020002, 0x8f420100, 0xaf420020,
+       0x8f430104, 0xaf4300a8, 0x9342010b, 0x93630000, 0x306300ff, 0x10710005,
+       0x304400ff, 0x10750006, 0x2c820016, 0x0a000227, 0x00000000, 0xaf940000,
+       0x0a000228, 0x2c820016, 0xaf930000, 0x0a000228, 0x00000000, 0xaf800000,
+       0x14400005, 0x00041880, 0x0e0002b2, 0x00000000, 0x0a000234, 0x00000000,
+       0x3c020800, 0x24424600, 0x00621821, 0x8c620000, 0x0040f809, 0x00000000,
+       0x10400005, 0x8fc20034, 0x8f420104, 0x3c016020, 0xac220014, 0x8fc20034,
+       0xaf520138, 0x24420001, 0xafc20034, 0x32020002, 0x10400019, 0x32020004,
+       0x8f420140, 0xaf420020, 0x93630000, 0x306300ff, 0x10710005, 0x00000000,
+       0x10750006, 0x00000000, 0x0a000250, 0x00000000, 0xaf940000, 0x0a000251,
+       0x00000000, 0xaf930000, 0x0a000251, 0x00000000, 0xaf800000, 0x0e0008b9,
+       0x00000000, 0x8ee20038, 0xaf520178, 0x24420001, 0xaee20038, 0x32020004,
+       0x1040ffad, 0x00000000, 0x8f420180, 0xaf420020, 0x93630000, 0x306300ff,
+       0x10710005, 0x00000000, 0x10750006, 0x00000000, 0x0a00026a, 0x00000000,
+       0xaf940000, 0x0a00026b, 0x00000000, 0xaf930000, 0x0a00026b, 0x00000000,
+       0xaf800000, 0x93620000, 0x14510004, 0x8ec2003c, 0x0e000835, 0x00000000,
+       0x8ec2003c, 0xaf5201b8, 0x24420001, 0x0a000206, 0xaec2003c, 0x27bdffe8,
+       0xafbf0010, 0x97420108, 0x24033000, 0x30447000, 0x10830012, 0x28823001,
+       0x10400007, 0x24024000, 0x1080000b, 0x24022000, 0x1082001a, 0x24020001,
+       0x0a000299, 0x00000000, 0x1082000c, 0x24025000, 0x1082000e, 0x00000000,
+       0x0a000299, 0x00000000, 0x0000000d, 0x0a00029b, 0x00001021, 0x0e000300,
+       0x00000000, 0x0a00029b, 0x00001021, 0x0e00048f, 0x00000000, 0x0a00029b,
+       0x00001021, 0x0e000fdf, 0x00000000, 0x0a00029b, 0x00001021, 0x0000000d,
+       0x00001021, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x93620000, 0x24030020,
+       0x304400ff, 0x10830005, 0x24020030, 0x10820007, 0x00000000, 0x0a0002af,
+       0x00000000, 0x2782000c, 0xaf820000, 0x03e00008, 0x00000000, 0x27820008,
+       0xaf820000, 0x03e00008, 0x00000000, 0xaf800000, 0x03e00008, 0x00000000,
+       0x0000000d, 0x03e00008, 0x00001021, 0x03e00008, 0x00001021, 0x27440100,
+       0x94830008, 0x30620004, 0x10400017, 0x30620002, 0x8f4202b8, 0x04430007,
+       0x8c820020, 0x3c040800, 0x8c830060, 0x24020001, 0x24630001, 0x03e00008,
+       0xac830060, 0xaf420280, 0x94830016, 0x3c060800, 0xa7430284, 0x8c850004,
+       0x8cc4005c, 0x00001021, 0x3c031000, 0x24840001, 0xaf450288, 0xaf4302b8,
+       0x03e00008, 0xacc4005c, 0x14400003, 0x3c040800, 0x03e00008, 0x00001021,
+       0x8c830084, 0x24020001, 0x24630001, 0x03e00008, 0xac830084, 0x27450100,
+       0x3c040800, 0x8c820088, 0x94a3000c, 0x24420001, 0x007a1821, 0xac820088,
+       0x8ca40018, 0x90664000, 0xaf440038, 0x8ca2001c, 0x2403fff8, 0x00063600,
+       0x00431024, 0x34420004, 0x3c030005, 0xaf42003c, 0xaf430030, 0x00000000,
+       0x00000000, 0x00000000, 0xaf460404, 0x00000000, 0x00000000, 0x00000000,
+       0x3c020006, 0x34420001, 0xaf420030, 0x00000000, 0x00000000, 0x00000000,
+       0x8f420000, 0x30420010, 0x1040fffd, 0x00001021, 0x03e00008, 0x00000000,
+       0x3c020800, 0x8c430020, 0x27bdffe8, 0xafb00010, 0x27500100, 0x1060001e,
+       0xafbf0014, 0x0e001006, 0x00000000, 0x8f830018, 0x8e020018, 0xac620000,
+       0x8f840018, 0x9602000c, 0xac820004, 0x8f830018, 0xac600008, 0x8f820018,
+       0xac40000c, 0x8f830018, 0xac600010, 0x8f820018, 0xac400014, 0x8f840018,
+       0x3c026000, 0x8c434448, 0xac830018, 0x96020008, 0x3c030800, 0x9464466e,
+       0x8f850018, 0x00021400, 0x00441025, 0x24040001, 0x0e001044, 0xaca2001c,
+       0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x27bdffc8, 0xafb3001c,
+       0x00009821, 0xafb7002c, 0x0000b821, 0xafbe0030, 0x0000f021, 0xafb50024,
+       0x27550100, 0xafbf0034, 0xafb60028, 0xafb40020, 0xafb20018, 0xafb10014,
+       0xafb00010, 0x96a20008, 0x8f540100, 0x8eb20018, 0x30420001, 0x10400037,
+       0x02a0b021, 0x8f630054, 0x2642ffff, 0x00431023, 0x18400006, 0x00000000,
+       0x0000000d, 0x00000000, 0x24000128, 0x0a000372, 0x00002021, 0x8f62004c,
+       0x02421023, 0x18400028, 0x00002021, 0x93650120, 0x93640121, 0x3c030800,
+       0x8c62008c, 0x308400ff, 0x24420001, 0x30a500ff, 0x00803821, 0x1485000b,
+       0xac62008c, 0x3c040800, 0x8c830090, 0x24630001, 0xac830090, 0x93620122,
+       0x30420001, 0x00021023, 0x30420005, 0x0a000372, 0x34440004, 0x27660100,
+       0x00041080, 0x00c21021, 0x8c430000, 0x02431823, 0x04600004, 0x24820001,
+       0x30440007, 0x1485fff9, 0x00041080, 0x10870007, 0x3c030800, 0xa3640121,
+       0x8c620094, 0x24040005, 0x24420001, 0x0a000372, 0xac620094, 0x24040004,
+       0x00809821, 0x9362003f, 0x304400ff, 0x38830016, 0x2c630001, 0x38820010,
+       0x2c420001, 0x00621825, 0x1460000c, 0x24020001, 0x38830008, 0x2c630001,
+       0x38820014, 0x2c420001, 0x00621825, 0x14600005, 0x24020001, 0x24020012,
+       0x14820002, 0x00001021, 0x24020001, 0x50400007, 0x8eb10020, 0x8ea20020,
+       0x8f630040, 0x00408821, 0x00431023, 0x5c400001, 0x8f710040, 0x9343010b,
+       0x24020004, 0x54620005, 0x36730080, 0x96a20008, 0x36730002, 0x24170001,
+       0x305e0020, 0x2402fffb, 0x02628024, 0x1200002a, 0x3c030800, 0x8c620030,
+       0x02021024, 0x10400026, 0x3c020800, 0x8c430020, 0x10600024, 0x32620004,
+       0x0e001006, 0x00000000, 0x8f830018, 0x8f420100, 0xac620000, 0x8f840018,
+       0x02201821, 0x32620002, 0xac900004, 0x8f840018, 0x50400001, 0x8ec30014,
+       0xac830008, 0x8f830018, 0x8ec20020, 0xac62000c, 0x8f840018, 0x8f620040,
+       0xac820010, 0x8f830018, 0x8ec20018, 0xac620014, 0x8f840018, 0x3c026000,
+       0x8c434448, 0x3c020800, 0xac830018, 0x9443466e, 0x8f840018, 0x3c024010,
+       0x00621825, 0xac83001c, 0x0e001044, 0x24040001, 0x32620004, 0x10400076,
+       0x00003821, 0x3c029000, 0x34420001, 0x3c038000, 0x02821025, 0xa360007c,
+       0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93620023,
+       0x30420080, 0x10400011, 0x00000000, 0x8f65005c, 0x8f63004c, 0x9764003c,
+       0x8f620064, 0x00a32823, 0x00852821, 0x00a2102b, 0x54400006, 0x3c023fff,
+       0x93620023, 0x3042007f, 0xa3620023, 0xaf720064, 0x3c023fff, 0x0a0003f1,
+       0x3442ffff, 0x8f62005c, 0x02421023, 0x04400011, 0x00000000, 0x8f65005c,
+       0x8f630064, 0x9764003c, 0x3c023fff, 0x3442ffff, 0xaf720064, 0x00a32823,
+       0x00852821, 0x0045102b, 0x10400004, 0x02451021, 0x3c053fff, 0x34a5ffff,
+       0x02451021, 0xaf62005c, 0x24070001, 0xaf72004c, 0x8f620054, 0x16420005,
+       0x00000000, 0x93620023, 0x30420040, 0x10400017, 0x24020001, 0x9762006a,
+       0x00022880, 0x50a00001, 0x24050001, 0x97630068, 0x93640081, 0x3c020800,
+       0x8c46004c, 0x00652821, 0x00852804, 0x00c5102b, 0x54400001, 0x00a03021,
+       0x3c020800, 0x8c440050, 0x00c4182b, 0x54600001, 0x00c02021, 0x8f420074,
+       0x2403fffe, 0x00832824, 0x00a21021, 0xaf62000c, 0x3c028000, 0x34420001,
+       0x02821025, 0xa3600081, 0xaf420020, 0x9363007e, 0x9362007a, 0x10620004,
+       0x00000000, 0x0e000f2a, 0x00000000, 0x00403821, 0x10e00017, 0x3c029000,
+       0x34420001, 0x02821025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024,
+       0x1440fffd, 0x3c028000, 0x9363007d, 0x34420001, 0x3c048000, 0x02821025,
+       0xa363007d, 0xaf420020, 0x8f4201f8, 0x00441024, 0x1440fffd, 0x24020002,
+       0x3c031000, 0xaf5401c0, 0xa34201c4, 0xaf4301f8, 0x8ea30014, 0x8f620040,
+       0x14430003, 0x00431023, 0x0a000443, 0x00001021, 0x28420001, 0x10400034,
+       0x00000000, 0x8f620040, 0xaf630040, 0x9362003e, 0x30420001, 0x1440000b,
+       0x3c029000, 0x93620022, 0x24420001, 0xa3620022, 0x93630022, 0x3c020800,
+       0x8c440098, 0x0064182b, 0x1460001e, 0x3c020800, 0x3c029000, 0x34420001,
+       0x02821025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd,
+       0x00000000, 0x3c038000, 0x9362007d, 0x34630001, 0x3c048000, 0x02831825,
+       0x34420001, 0xa362007d, 0xaf430020, 0x8f4201f8, 0x00441024, 0x1440fffd,
+       0x24020002, 0x3c031000, 0xaf5401c0, 0xa34201c4, 0x24020001, 0xaf4301f8,
+       0xa7620012, 0x0a000476, 0xa3600022, 0x9743007a, 0x9444002a, 0x00641821,
+       0x3063fffe, 0xa7630012, 0x0e000b68, 0x00000000, 0x12e00003, 0x00000000,
+       0x0e000f27, 0x00000000, 0x53c00004, 0x96a20008, 0x0e000c10, 0x00000000,
+       0x96a20008, 0x8fbf0034, 0x8fbe0030, 0x8fb7002c, 0x8fb60028, 0x8fb50024,
+       0x8fb40020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x00021042,
+       0x30420001, 0x03e00008, 0x27bd0038, 0x27bdffe8, 0xafbf0010, 0x97420108,
+       0x2403000b, 0x304400ff, 0x1083004e, 0x2882000c, 0x10400011, 0x24020006,
+       0x1082003e, 0x28820007, 0x10400007, 0x28820008, 0x1080002b, 0x24020001,
+       0x1082002e, 0x3c026000, 0x0a000504, 0x00000000, 0x14400061, 0x2882000a,
+       0x1440002b, 0x00000000, 0x0a0004ec, 0x00000000, 0x2402001c, 0x1082004e,
+       0x2882001d, 0x1040000e, 0x24020019, 0x10820041, 0x2882001a, 0x10400005,
+       0x2402000e, 0x10820036, 0x00000000, 0x0a000504, 0x00000000, 0x2402001b,
+       0x1082003c, 0x00000000, 0x0a000504, 0x00000000, 0x240200c1, 0x10820040,
+       0x288200c2, 0x10400005, 0x24020080, 0x1082001f, 0x00000000, 0x0a000504,
+       0x00000000, 0x240200c2, 0x1082003b, 0x00000000, 0x0a000504, 0x00000000,
+       0x3c026000, 0x0e000c7d, 0xac400808, 0x0a000506, 0x8fbf0010, 0x8c444448,
+       0x3c030800, 0xac640064, 0x0e000c7d, 0x00000000, 0x3c026000, 0x8c444448,
+       0x3c030800, 0x0a000505, 0xac640068, 0x8f440100, 0x0e000508, 0x00000000,
+       0x3c026000, 0x8c444448, 0x3c030800, 0x0a000505, 0xac64006c, 0x0e000cab,
+       0x00000000, 0x0a000506, 0x8fbf0010, 0x8f440100, 0x0e000cd5, 0x00000000,
+       0x0a000506, 0x8fbf0010, 0x0e000d1c, 0x00000000, 0x0a000506, 0x8fbf0010,
+       0x0000000d, 0x0a000506, 0x8fbf0010, 0x0e0005d7, 0x00000000, 0x0a000506,
+       0x8fbf0010, 0x8f440100, 0x0e000d7e, 0x00000000, 0x0a000506, 0x8fbf0010,
+       0x0e000e95, 0x00000000, 0x0a000506, 0x8fbf0010, 0x0e000626, 0x00000000,
+       0x0a000506, 0x8fbf0010, 0x0e000b68, 0x00000000, 0x0a000506, 0x8fbf0010,
+       0x0000000d, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x27bdffe8, 0x3c029000,
+       0x34420001, 0xafb00010, 0x00808021, 0x02021025, 0x3c038000, 0xafbf0014,
+       0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93620005,
+       0x34420001, 0xa3620005, 0x8f63004c, 0x8f620054, 0x10620019, 0x3c028000,
+       0x9762006a, 0x00022880, 0x50a00001, 0x24050001, 0x97630068, 0x93640081,
+       0x3c020800, 0x8c46004c, 0x00652821, 0x00852804, 0x00c5102b, 0x54400001,
+       0x00a03021, 0x3c020800, 0x8c440050, 0x00c4182b, 0x54600001, 0x00c02021,
+       0x8f420074, 0x2403fffe, 0x00832824, 0x00a21021, 0xaf62000c, 0x3c028000,
+       0x34420001, 0x02021025, 0x0e000c7d, 0xaf420020, 0x3c029000, 0x34420001,
+       0x3c038000, 0x02021025, 0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd,
+       0x3c028000, 0x9363007d, 0x34420001, 0x3c048000, 0x02021025, 0xa363007d,
+       0xaf420020, 0x8f4201f8, 0x00441024, 0x1440fffd, 0x8fbf0014, 0xaf5001c0,
+       0x8fb00010, 0x24020002, 0x3c031000, 0xa34201c4, 0xaf4301f8, 0x03e00008,
+       0x27bd0018, 0x27bdffd8, 0xafbf0020, 0xafb3001c, 0xafb20018, 0xafb10014,
+       0xafb00010, 0x93630005, 0x00809021, 0x24020030, 0x30630030, 0x14620072,
+       0x00a09821, 0x3c020800, 0x8c430020, 0x1060006c, 0x00000000, 0x0e001006,
+       0x00000000, 0x8f820018, 0xac520000, 0x9363003e, 0x9362003f, 0x8f840018,
+       0x00031a00, 0x00431025, 0xac820004, 0x93630081, 0x93620082, 0x8f850018,
+       0x00031e00, 0x00021400, 0x00621825, 0xaca30008, 0x8f840018, 0x8f620040,
+       0xac82000c, 0x8f830018, 0x8f620048, 0xac620010, 0x8f840018, 0x8f62004c,
+       0x3c110800, 0xac820014, 0x8f830018, 0x8f620050, 0x26304660, 0x00002021,
+       0xac620018, 0x9602000e, 0x8f850018, 0x3c03c00b, 0x00431025, 0x0e001044,
+       0xaca2001c, 0x8f830018, 0x8f620054, 0xac620000, 0x8f840018, 0x8f620058,
+       0xac820004, 0x8f830018, 0x8f62005c, 0xac620008, 0x8f840018, 0x8f620060,
+       0xac82000c, 0x8f850018, 0x8f620064, 0xaca20010, 0x97630068, 0x9762006a,
+       0x8f840018, 0x00031c00, 0x00431025, 0xac820014, 0x8f830018, 0x00002021,
+       0xac600018, 0x9602000e, 0x8f850018, 0x3c03c00c, 0x00431025, 0x0e001044,
+       0xaca2001c, 0x8f840018, 0x8f630018, 0xac830000, 0x936200c4, 0x30420002,
+       0x10400006, 0x00000000, 0x976200c8, 0x8f830018, 0x3042ffff, 0x0a0005b5,
+       0xac620004, 0x8f820018, 0xac400004, 0x8f830018, 0x8f62006c, 0xac620008,
+       0x8f840018, 0x8f6200dc, 0xac82000c, 0x8f830018, 0xac600010, 0x93620005,
+       0x8f830018, 0x00021600, 0x00531025, 0xac620014, 0x8f850018, 0x3c026000,
+       0x8c434448, 0x24040001, 0x26224660, 0xaca30018, 0x9443000e, 0x8f850018,
+       0x3c02400d, 0x00621825, 0x0e001044, 0xaca3001c, 0x0e000d48, 0x02402021,
+       0x8fbf0020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x03e00008,
+       0x27bd0028, 0x27bdffe0, 0xafb00010, 0x27500100, 0xafbf0018, 0xafb10014,
+       0x9603000c, 0x240200c1, 0x5462001d, 0x8e040000, 0x3c029000, 0x8f440100,
+       0x34420001, 0x3c038000, 0x00821025, 0xaf420020, 0x8f420020, 0x00431024,
+       0x1440fffd, 0x00000000, 0x3c038000, 0x9362007d, 0x34630001, 0x3c058000,
+       0x00831825, 0x34420004, 0xa362007d, 0xaf430020, 0x8f4201f8, 0x00451024,
+       0x1440fffd, 0x24020002, 0x3c031000, 0xaf4401c0, 0xa34201c4, 0xaf4301f8,
+       0x0a000622, 0x8fbf0018, 0x8f65004c, 0x24060001, 0x0e000db5, 0x2407049f,
+       0x3c020800, 0x8c430020, 0x9611000c, 0x1060001d, 0x8e100000, 0x0e001006,
+       0x00000000, 0x8f820018, 0xac500000, 0x8f840018, 0x00111400, 0xac820004,
+       0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010,
+       0x8f840018, 0x240204a2, 0xac820014, 0x8f850018, 0x3c026000, 0x8c434448,
+       0x24040001, 0x3c020800, 0xaca30018, 0x9443466e, 0x8f850018, 0x3c024019,
+       0x00621825, 0x0e001044, 0xaca3001c, 0x8fbf0018, 0x8fb10014, 0x8fb00010,
+       0x03e00008, 0x27bd0020, 0x27bdffb0, 0xafb1002c, 0x27510100, 0xafbf004c,
+       0xafbe0048, 0xafb70044, 0xafb60040, 0xafb5003c, 0xafb40038, 0xafb30034,
+       0xafb20030, 0xafb00028, 0x8e350000, 0x9634000c, 0x3c026000, 0x8c434448,
+       0x0000f021, 0xaf630170, 0x8f620040, 0x8e230014, 0x0000b821, 0x00431023,
+       0x044001ec, 0x0000b021, 0x32820010, 0x1040002e, 0x3c026000, 0x9363003f,
+       0x9222000e, 0x10430006, 0x2402000c, 0x9223000f, 0x10620003, 0x24020014,
+       0x14620025, 0x3c026000, 0x32820004, 0x10400007, 0x241e0001, 0x8f620050,
+       0x24420001, 0xaf620050, 0x8f630054, 0x24630001, 0xaf630054, 0x32830102,
+       0x24020002, 0x5462000d, 0x9222000f, 0x8f620040, 0x24420001, 0xaf620040,
+       0x8f630048, 0x8f620040, 0x24630001, 0x54620005, 0x9222000f, 0x8f620048,
+       0x24420001, 0xaf620048, 0x9222000f, 0xa362003f, 0x9223000f, 0x24020012,
+       0x14620007, 0x3c026000, 0x3c030800, 0x8c620074, 0x24420001, 0x0e000f6e,
+       0xac620074, 0x3c026000, 0x8c434448, 0x32820040, 0xaf630174, 0x32830020,
+       0xafa30010, 0x32830080, 0xafa30014, 0x32830001, 0xafa3001c, 0x32830008,
+       0xafa30020, 0x32830100, 0x104000bb, 0xafa30018, 0x8e260010, 0x8f630054,
+       0x24c2ffff, 0x00431023, 0x18400006, 0x00000000, 0x0000000d, 0x00000000,
+       0x24000128, 0x0a0006b2, 0x00009021, 0x8f62004c, 0x00c21023, 0x18400028,
+       0x00009021, 0x93650120, 0x93640121, 0x3c030800, 0x8c62008c, 0x308400ff,
+       0x24420001, 0x30a500ff, 0x00804021, 0x1485000b, 0xac62008c, 0x3c040800,
+       0x8c830090, 0x24630001, 0xac830090, 0x93620122, 0x30420001, 0x00021023,
+       0x30420005, 0x0a0006b2, 0x34520004, 0x27670100, 0x00041080, 0x00e21021,
+       0x8c430000, 0x00c31823, 0x04600004, 0x24820001, 0x30440007, 0x1485fff9,
+       0x00041080, 0x10880007, 0x3c030800, 0xa3640121, 0x8c620094, 0x24120005,
+       0x24420001, 0x0a0006b2, 0xac620094, 0x24120004, 0x32420001, 0x10400020,
+       0x3c020800, 0x8c430020, 0x8e300000, 0x1060001c, 0x8e330010, 0x0e001006,
+       0x00000000, 0x8f820018, 0xac500000, 0x8f840018, 0x24020001, 0xac820004,
+       0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010,
+       0x8f820018, 0xac530014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001,
+       0x3c020800, 0xaca30018, 0x9443466e, 0x8f850018, 0x3c024010, 0x00621825,
+       0x0e001044, 0xaca3001c, 0x32420004, 0x10400060, 0x00003821, 0x3c029000,
+       0x8e260010, 0x34420001, 0x3c038000, 0x02a21025, 0xa360007c, 0xaf420020,
+       0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93620023, 0x30420080,
+       0x10400011, 0x00000000, 0x8f65005c, 0x8f63004c, 0x9764003c, 0x8f620064,
+       0x00a32823, 0x00852821, 0x00a2102b, 0x54400006, 0x3c023fff, 0x93620023,
+       0x3042007f, 0xa3620023, 0xaf660064, 0x3c023fff, 0x0a000702, 0x3442ffff,
+       0x8f62005c, 0x00c21023, 0x04400011, 0x00000000, 0x8f65005c, 0x8f630064,
+       0x9764003c, 0x3c023fff, 0x3442ffff, 0xaf660064, 0x00a32823, 0x00852821,
+       0x0045102b, 0x10400004, 0x00c51021, 0x3c053fff, 0x34a5ffff, 0x00c51021,
+       0xaf62005c, 0x24070001, 0xaf66004c, 0x8f620054, 0x14c20005, 0x00000000,
+       0x93620023, 0x30420040, 0x10400017, 0x24020001, 0x9762006a, 0x00022880,
+       0x50a00001, 0x24050001, 0x97630068, 0x93640081, 0x3c020800, 0x8c46004c,
+       0x00652821, 0x00852804, 0x00c5102b, 0x54400001, 0x00a03021, 0x3c020800,
+       0x8c440050, 0x00c4182b, 0x54600001, 0x00c02021, 0x8f420074, 0x2403fffe,
+       0x00832824, 0x00a21021, 0xaf62000c, 0x3c028000, 0x34420001, 0x02a21025,
+       0xa3600081, 0xaf420020, 0x9363007e, 0x9362007a, 0x10620005, 0x00e0b021,
+       0x0e000f2a, 0x00000000, 0x00403821, 0x00e0b021, 0x8fa20010, 0x10400008,
+       0x00000000, 0x8e220018, 0xaf620018, 0x8e23001c, 0xaf63001c, 0x8e220020,
+       0x24160001, 0xaf620058, 0x13c00036, 0x32820004, 0x10400035, 0x8fa30014,
+       0x93620023, 0x30420040, 0x10400031, 0x3c020800, 0x8c430020, 0x1060001c,
+       0x8e300000, 0x0e001006, 0x00000000, 0x8f820018, 0xac500000, 0x8f830018,
+       0xac600004, 0x8f820018, 0xac400008, 0x8f830018, 0xac60000c, 0x8f820018,
+       0xac400010, 0x8f830018, 0x24020587, 0xac620014, 0x8f850018, 0x3c026000,
+       0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x9443466e, 0x8f850018,
+       0x3c024019, 0x00621825, 0x0e001044, 0xaca3001c, 0x3c029000, 0x34420001,
+       0x02a21025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd,
+       0x24020001, 0xaf62000c, 0x93630023, 0x3c028000, 0x34420001, 0x02a21025,
+       0x306300bf, 0xa3630023, 0xaf420020, 0x8fa30014, 0x10600012, 0x8fa3001c,
+       0x9362007c, 0x24420001, 0xa362007c, 0x9363007e, 0x9362007a, 0x1462000b,
+       0x8fa3001c, 0x9362007c, 0x3c030800, 0x8c640024, 0x0044102b, 0x14400005,
+       0x8fa3001c, 0x0e000f2a, 0x00000000, 0x02c2b025, 0x8fa3001c, 0x3062ffff,
+       0x10400003, 0x32820200, 0x0a000793, 0x24170004, 0x10400003, 0x00000000,
+       0x24170040, 0x24160001, 0x13c0005d, 0x32820002, 0x1040005c, 0x8fa20020,
+       0x9222000a, 0x30420020, 0x10400033, 0x3c100800, 0x93620023, 0x30420040,
+       0x1040002f, 0x8e020020, 0x1040001e, 0x3c029000, 0x0e001006, 0x00000000,
+       0x8f820018, 0xac550000, 0x8f840018, 0x3c02008d, 0xac820004, 0x8f830018,
+       0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, 0x8f840018,
+       0x240205bf, 0xac820014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001,
+       0x3c020800, 0xaca30018, 0x9443466e, 0x8f850018, 0x3c024019, 0x00621825,
+       0x0e001044, 0xaca3001c, 0x3c029000, 0x34420001, 0x02a21025, 0xaf420020,
+       0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93630023,
+       0x3c028000, 0x34420001, 0x02a21025, 0x306300bf, 0xa3630023, 0xaf420020,
+       0x8e020020, 0x10400023, 0x8fa20020, 0x0e001006, 0x00000000, 0x8f840018,
+       0x8e230000, 0xac830000, 0x9222000a, 0x8f830018, 0x00021600, 0xac620004,
+       0x8f840018, 0x8f620040, 0xac820008, 0x8f850018, 0x8f63004c, 0xaca3000c,
+       0x9362003f, 0x8f840018, 0x304200ff, 0xac820010, 0x8f830018, 0x3c026000,
+       0xac600014, 0x8f850018, 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018,
+       0x9443466e, 0x8f850018, 0x3c02401a, 0x00621825, 0x0e001044, 0xaca3001c,
+       0x8fa20020, 0x1040000e, 0x8fa20018, 0x9222000a, 0xa3620082, 0x56e00005,
+       0x36f70008, 0x8fa30018, 0x10600004, 0x00000000, 0x36f70008, 0x0a000801,
+       0x24160001, 0x0e000de1, 0x02a02021, 0x8fa20018, 0x10400003, 0x00000000,
+       0x36f70010, 0x24160001, 0x12c00019, 0x3c029000, 0x34420001, 0x02a21025,
+       0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000,
+       0x3c038000, 0x9362007d, 0x34630001, 0x3c048000, 0x02a31825, 0x02e21025,
+       0xa362007d, 0xaf430020, 0x8f4201f8, 0x00441024, 0x1440fffd, 0x24020002,
+       0x3c031000, 0xaf5501c0, 0xa34201c4, 0xaf4301f8, 0x9363003f, 0x24020012,
+       0x14620004, 0x3c026000, 0x0e000f6e, 0x00000000, 0x3c026000, 0x8c434448,
+       0xaf630178, 0x8fbf004c, 0x8fbe0048, 0x8fb70044, 0x8fb60040, 0x8fb5003c,
+       0x8fb40038, 0x8fb30034, 0x8fb20030, 0x8fb1002c, 0x8fb00028, 0x03e00008,
+       0x27bd0050, 0x27bdffe8, 0xafbf0014, 0xafb00010, 0x8f500180, 0x97420184,
+       0x30420200, 0x14400015, 0x00000000, 0x8f430188, 0x3c02ff00, 0x00621824,
+       0x3c020200, 0x10620031, 0x0043102b, 0x14400007, 0x3c020300, 0x1060000b,
+       0x3c020100, 0x1062000d, 0x00000000, 0x0a0008b4, 0x00000000, 0x10620027,
+       0x3c020400, 0x1062003e, 0x02002021, 0x0a0008b4, 0x00000000, 0x0e000e1e,
+       0x02002021, 0x0a0008b6, 0x8fbf0014, 0x93620005, 0x30420020, 0x1440005e,
+       0x8fbf0014, 0x3c029000, 0x34420001, 0x02021025, 0xaf420020, 0x3c038000,
+       0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93620005, 0x3c038000,
+       0x34630001, 0x02031825, 0x34420020, 0xa3620005, 0xaf430020, 0x93620005,
+       0x30420020, 0x14400003, 0x02002021, 0x0000000d, 0x02002021, 0x0e000553,
+       0x24055854, 0x0a0008b6, 0x8fbf0014, 0x93620005, 0x30420001, 0x1040003f,
+       0x3c029000, 0x34420001, 0x02021025, 0xaf420020, 0x3c038000, 0x8f420020,
+       0x00431024, 0x1440fffd, 0x00000000, 0x93620005, 0x3c048000, 0x3c030800,
+       0x304200fe, 0xa3620005, 0x8c620020, 0x34840001, 0x02042025, 0xaf440020,
+       0x1040002d, 0x8fbf0014, 0x0a000894, 0x00000000, 0x00002821, 0x00003021,
+       0x0e000f78, 0x240706a4, 0x3c020800, 0x8c430020, 0x10600023, 0x8fbf0014,
+       0x0e001006, 0x00000000, 0x8f820018, 0xac500000, 0x93630082, 0x9362003f,
+       0x8f840018, 0x00031a00, 0x00431025, 0xac820004, 0x8f830018, 0xac600008,
+       0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, 0x8f820018, 0xac400014,
+       0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018,
+       0x9443466e, 0x8f850018, 0x3c02400a, 0x00621825, 0x0e001044, 0xaca3001c,
+       0x0a0008b6, 0x8fbf0014, 0x0000000d, 0x8fbf0014, 0x8fb00010, 0x03e00008,
+       0x27bd0018, 0x27bdffe8, 0xafbf0010, 0x93420148, 0x2444ffff, 0x2c830005,
+       0x10600047, 0x3c020800, 0x24424598, 0x00041880, 0x00621821, 0x8c640000,
+       0x00800008, 0x00000000, 0x8f430144, 0x8f62000c, 0x14620006, 0x24020001,
+       0xaf62000c, 0x0e000909, 0x00000000, 0x0a000907, 0x8fbf0010, 0x8f62000c,
+       0x0a000900, 0x00000000, 0x97630010, 0x8f420144, 0x14430006, 0x24020001,
+       0xa7620010, 0x0e000eeb, 0x00000000, 0x0a000907, 0x8fbf0010, 0x97620010,
+       0x0a000900, 0x00000000, 0x97630012, 0x8f420144, 0x14430006, 0x24020001,
+       0xa7620012, 0x0e000f06, 0x00000000, 0x0a000907, 0x8fbf0010, 0x97620012,
+       0x0a000900, 0x00000000, 0x97630014, 0x8f420144, 0x14430006, 0x24020001,
+       0xa7620014, 0x0e000f21, 0x00000000, 0x0a000907, 0x8fbf0010, 0x97620014,
+       0x0a000900, 0x00000000, 0x97630016, 0x8f420144, 0x14430006, 0x24020001,
+       0xa7620016, 0x0e000f24, 0x00000000, 0x0a000907, 0x8fbf0010, 0x97620016,
+       0x14400006, 0x8fbf0010, 0x3c030800, 0x8c620070, 0x24420001, 0xac620070,
+       0x8fbf0010, 0x03e00008, 0x27bd0018, 0x27bdffe8, 0xafbf0010, 0x93620081,
+       0x3c030800, 0x8c640048, 0x0044102b, 0x14400028, 0x3c029000, 0x8f460140,
+       0x34420001, 0x3c038000, 0x00c21025, 0xaf420020, 0x8f420020, 0x00431024,
+       0x1440fffd, 0x3c048000, 0x34840001, 0x3c059000, 0x34a50001, 0x3c078000,
+       0x24020012, 0x24030080, 0x00c42025, 0x00c52825, 0xa362003f, 0xa3630082,
+       0xaf440020, 0xaf450020, 0x8f420020, 0x00471024, 0x1440fffd, 0x3c038000,
+       0x9362007d, 0x34630001, 0x3c048000, 0x00c31825, 0x34420020, 0xa362007d,
+       0xaf430020, 0x8f4201f8, 0x00441024, 0x1440fffd, 0x24020002, 0x3c031000,
+       0x0a00096d, 0xaf4601c0, 0x93620081, 0x24420001, 0x0e000f2a, 0xa3620081,
+       0x9763006a, 0x00032880, 0x14a00002, 0x00403821, 0x24050001, 0x97630068,
+       0x93640081, 0x3c020800, 0x8c46004c, 0x00652821, 0x00852804, 0x00c5102b,
+       0x54400001, 0x00a03021, 0x3c020800, 0x8c440050, 0x00c4182b, 0x54600001,
+       0x00c02021, 0x8f420074, 0x2403fffe, 0x00832824, 0x00a21021, 0xaf62000c,
+       0x10e0001a, 0x3c029000, 0x8f440140, 0x34420001, 0x3c038000, 0x00821025,
+       0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x3c038000,
+       0x9362007d, 0x34630001, 0x3c058000, 0x00831825, 0x34420004, 0xa362007d,
+       0xaf430020, 0x8f4201f8, 0x00451024, 0x1440fffd, 0x24020002, 0x3c031000,
+       0xaf4401c0, 0xa34201c4, 0xaf4301f8, 0x8fbf0010, 0x03e00008, 0x27bd0018,
+       0x27bdffd8, 0xafb3001c, 0x27530100, 0xafbf0024, 0xafb40020, 0xafb20018,
+       0xafb10014, 0xafb00010, 0x96620008, 0x3c140800, 0x8f520100, 0x30420001,
+       0x104000cf, 0x00000000, 0x8e700018, 0x8f630054, 0x2602ffff, 0x00431023,
+       0x18400006, 0x00000000, 0x0000000d, 0x00000000, 0x24000128, 0x0a0009b6,
+       0x00008821, 0x8f62004c, 0x02021023, 0x18400028, 0x00008821, 0x93650120,
+       0x93640121, 0x3c030800, 0x8c62008c, 0x308400ff, 0x24420001, 0x30a500ff,
+       0x00803821, 0x1485000b, 0xac62008c, 0x3c040800, 0x8c830090, 0x24630001,
+       0xac830090, 0x93620122, 0x30420001, 0x00021023, 0x30420005, 0x0a0009b6,
+       0x34510004, 0x27660100, 0x00041080, 0x00c21021, 0x8c430000, 0x02031823,
+       0x04600004, 0x24820001, 0x30440007, 0x1485fff9, 0x00041080, 0x10870007,
+       0x3c030800, 0xa3640121, 0x8c620094, 0x24110005, 0x24420001, 0x0a0009b6,
+       0xac620094, 0x24110004, 0x32220001, 0x1040001e, 0x8e820020, 0x1040001d,
+       0x32220004, 0x0e001006, 0x00000000, 0x8f820018, 0xac520000, 0x8f840018,
+       0x24020001, 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c,
+       0x8f830018, 0xac600010, 0x8f820018, 0xac500014, 0x8f850018, 0x3c026000,
+       0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x9443466e, 0x8f850018,
+       0x3c024010, 0x00621825, 0x0e001044, 0xaca3001c, 0x32220004, 0x10400076,
+       0x00003821, 0x3c029000, 0x34420001, 0x3c038000, 0x02421025, 0xa360007c,
+       0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93620023,
+       0x30420080, 0x10400011, 0x00000000, 0x8f65005c, 0x8f63004c, 0x9764003c,
+       0x8f620064, 0x00a32823, 0x00852821, 0x00a2102b, 0x54400006, 0x3c023fff,
+       0x93620023, 0x3042007f, 0xa3620023, 0xaf700064, 0x3c023fff, 0x0a000a03,
+       0x3442ffff, 0x8f62005c, 0x02021023, 0x04400011, 0x00000000, 0x8f65005c,
+       0x8f630064, 0x9764003c, 0x3c023fff, 0x3442ffff, 0xaf700064, 0x00a32823,
+       0x00852821, 0x0045102b, 0x10400004, 0x02051021, 0x3c053fff, 0x34a5ffff,
+       0x02051021, 0xaf62005c, 0x24070001, 0xaf70004c, 0x8f620054, 0x16020005,
+       0x00000000, 0x93620023, 0x30420040, 0x10400017, 0x24020001, 0x9762006a,
+       0x00022880, 0x50a00001, 0x24050001, 0x97630068, 0x93640081, 0x3c020800,
+       0x8c46004c, 0x00652821, 0x00852804, 0x00c5102b, 0x54400001, 0x00a03021,
+       0x3c020800, 0x8c440050, 0x00c4182b, 0x54600001, 0x00c02021, 0x8f420074,
+       0x2403fffe, 0x00832824, 0x00a21021, 0xaf62000c, 0x3c028000, 0x34420001,
+       0x02421025, 0xa3600081, 0xaf420020, 0x9363007e, 0x9362007a, 0x10620004,
+       0x00000000, 0x0e000f2a, 0x00000000, 0x00403821, 0x10e00017, 0x3c029000,
+       0x34420001, 0x02421025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024,
+       0x1440fffd, 0x3c028000, 0x9363007d, 0x34420001, 0x3c048000, 0x02421025,
+       0xa363007d, 0xaf420020, 0x8f4201f8, 0x00441024, 0x1440fffd, 0x24020002,
+       0x3c031000, 0xaf5201c0, 0xa34201c4, 0xaf4301f8, 0x9342010b, 0x8e830020,
+       0x27500100, 0x38420006, 0x10600029, 0x2c510001, 0x0e001006, 0x00000000,
+       0x8f830018, 0x8e020000, 0xac620000, 0x8f840018, 0x96020008, 0xac820004,
+       0x8f830018, 0x8e020014, 0xac620008, 0x8f850018, 0x3c026000, 0x8c434448,
+       0xaca3000c, 0x8f840018, 0x96020012, 0xac820010, 0x8f850018, 0x8e030020,
+       0xaca30014, 0x9602000c, 0x9603000e, 0x8f840018, 0x00021400, 0x00431025,
+       0xac820018, 0x12200005, 0x3c020800, 0x9443466e, 0x8f840018, 0x0a000a78,
+       0x3c024013, 0x9443466e, 0x8f840018, 0x3c024014, 0x00621825, 0xac83001c,
+       0x0e001044, 0x24040001, 0x8e630014, 0x8f620040, 0x14430003, 0x00431023,
+       0x0a000a83, 0x00001021, 0x28420001, 0x10400034, 0x00000000, 0x8f620040,
+       0xaf630040, 0x9362003e, 0x30420001, 0x1440000b, 0x3c029000, 0x93620022,
+       0x24420001, 0xa3620022, 0x93630022, 0x3c020800, 0x8c440098, 0x0064182b,
+       0x1460001e, 0x3c020800, 0x3c029000, 0x34420001, 0x02421025, 0xaf420020,
+       0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x3c038000,
+       0x9362007d, 0x34630001, 0x3c048000, 0x02431825, 0x34420001, 0xa362007d,
+       0xaf430020, 0x8f4201f8, 0x00441024, 0x1440fffd, 0x24020002, 0x3c031000,
+       0xaf5201c0, 0xa34201c4, 0x24020001, 0xaf4301f8, 0xa7620012, 0x0a000ab6,
+       0xa3600022, 0x9743007a, 0x9444002a, 0x00641821, 0x3063fffe, 0xa7630012,
+       0x0e000b68, 0x00000000, 0x97420108, 0x8fbf0024, 0x8fb40020, 0x8fb3001c,
+       0x8fb20018, 0x8fb10014, 0x8fb00010, 0x00021042, 0x30420001, 0x03e00008,
+       0x27bd0028, 0x27bdffe0, 0xafb20018, 0x3c120800, 0x8e420020, 0xafb00010,
+       0x27500100, 0xafbf001c, 0x10400046, 0xafb10014, 0x0e001006, 0x00000000,
+       0x8f840018, 0x8e020000, 0xac820000, 0x936300b1, 0x936200c5, 0x8f850018,
+       0x00031e00, 0x00021400, 0x34420100, 0x00621825, 0xaca30004, 0x8f840018,
+       0x8e02001c, 0xac820008, 0x8f830018, 0x8f620048, 0xac62000c, 0x8f840018,
+       0x96020012, 0xac820010, 0x8f830018, 0x8f620040, 0x24040001, 0xac620014,
+       0x8f850018, 0x3c026000, 0x8c434448, 0x3c020800, 0x24514660, 0xaca30018,
+       0x9623000e, 0x8f850018, 0x3c024016, 0x00621825, 0x0e001044, 0xaca3001c,
+       0x96030008, 0x30630010, 0x1060001c, 0x8e420020, 0x1040001a, 0x8e100000,
+       0x0e001006, 0x00000000, 0x8f820018, 0xac500000, 0x8f830018, 0xac600004,
+       0x8f820018, 0xac400008, 0x8f830018, 0xac60000c, 0x8f820018, 0xac400010,
+       0x8f830018, 0xac600014, 0x8f850018, 0x3c036000, 0x8c634448, 0x24040001,
+       0xaca30018, 0x9622000e, 0x8f850018, 0x3c034015, 0x00431025, 0x0e001044,
+       0xaca2001c, 0x00001021, 0x8fbf001c, 0x8fb20018, 0x8fb10014, 0x8fb00010,
+       0x03e00008, 0x27bd0020, 0x27bdffe0, 0xafb20018, 0x3c120800, 0x8e420020,
+       0xafb00010, 0x27500100, 0xafbf001c, 0x10400041, 0xafb10014, 0x0e001006,
+       0x00000000, 0x8f830018, 0x8e020000, 0xac620000, 0x8f840018, 0x24020100,
+       0xac820004, 0x8f830018, 0x8e02001c, 0xac620008, 0x8f840018, 0x8e020018,
+       0xac82000c, 0x8f830018, 0x96020012, 0xac620010, 0x8f840018, 0x96020008,
+       0xac820014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800,
+       0x24514660, 0xaca30018, 0x9623000e, 0x8f850018, 0x3c024017, 0x00621825,
+       0x0e001044, 0xaca3001c, 0x96030008, 0x30630010, 0x1060001c, 0x8e420020,
+       0x1040001a, 0x8e100000, 0x0e001006, 0x00000000, 0x8f820018, 0xac500000,
+       0x8f830018, 0xac600004, 0x8f820018, 0xac400008, 0x8f830018, 0xac60000c,
+       0x8f820018, 0xac400010, 0x8f830018, 0xac600014, 0x8f850018, 0x3c036000,
+       0x8c634448, 0x24040001, 0xaca30018, 0x9622000e, 0x8f850018, 0x3c034015,
+       0x00431025, 0x0e001044, 0xaca2001c, 0x00001021, 0x8fbf001c, 0x8fb20018,
+       0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x27bdffe8, 0xafbf0010,
+       0x936200c4, 0x30420002, 0x10400019, 0x00000000, 0x936200c5, 0x936300b1,
+       0x00431023, 0x304400ff, 0x30830080, 0x10600004, 0x00000000, 0x0000000d,
+       0x00000000, 0x24000a6a, 0x93620004, 0x00441023, 0x304400ff, 0x30830080,
+       0x10600004, 0x2482ffff, 0x8f650024, 0x0a000b82, 0x00000000, 0x00022b00,
+       0x8f620024, 0x0045102b, 0x10400002, 0x00000000, 0x8f650024, 0x8f620048,
+       0x8f630040, 0x00431823, 0x0065202b, 0x10800004, 0x00000000, 0x8f620040,
+       0x00451021, 0xaf620048, 0x9762003c, 0x0062102b, 0x10400041, 0x8fbf0010,
+       0x10a0003f, 0x3c029000, 0x34420001, 0x3c040800, 0x8c830080, 0x8f450100,
+       0x3c068000, 0x24630001, 0x00a21025, 0xac830080, 0xaf420020, 0x8f420020,
+       0x00461024, 0x1440fffd, 0x3c038000, 0x9362007d, 0x34630001, 0x3c048000,
+       0x00a31825, 0x34420004, 0xa362007d, 0xaf430020, 0x8f4201f8, 0x00441024,
+       0x1440fffd, 0x24020002, 0x3c030800, 0xaf4501c0, 0xa34201c4, 0x8c640020,
+       0x3c021000, 0xaf4201f8, 0x1080001f, 0x8fbf0010, 0x0e001006, 0x00000000,
+       0x8f830018, 0x8f420100, 0xac620000, 0x8f840018, 0x8f620040, 0xac820004,
+       0x8f850018, 0x8f620048, 0xaca20008, 0x8f830018, 0xac60000c, 0x8f820018,
+       0xac400010, 0x8f830018, 0x3c026000, 0xac600014, 0x8f840018, 0x8c434448,
+       0x3c020800, 0xac830018, 0x9443466e, 0x8f840018, 0x3c0240c2, 0x00621825,
+       0xac83001c, 0x0e001044, 0x24040001, 0x8fbf0010, 0x03e00008, 0x27bd0018,
+       0x3c020800, 0x24423958, 0xaf82000c, 0x03e00008, 0x00000000, 0x27bdffe8,
+       0xafb00010, 0x27500100, 0xafbf0014, 0x8e02001c, 0x14400003, 0x3c020800,
+       0x0000000d, 0x3c020800, 0x8c430020, 0x10600026, 0x00001021, 0x0e001006,
+       0x00000000, 0x8f830018, 0x8e020000, 0xac620000, 0x8f840018, 0x8e02001c,
+       0xac820004, 0x8f830018, 0xac600008, 0x8f840018, 0x8e020018, 0xac82000c,
+       0x8f850018, 0x96020012, 0xaca20010, 0x8f830018, 0x3c106000, 0xac600014,
+       0x8f840018, 0x8e024448, 0x3c030800, 0xac820018, 0x9462466e, 0x8f840018,
+       0x3c034012, 0x00431025, 0xac82001c, 0x0e001044, 0x24040001, 0x8e036800,
+       0x00001021, 0x3c040001, 0x00641825, 0xae036800, 0x0a000c0d, 0x8fbf0014,
+       0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x3c020800, 0x97430078,
+       0x9444002e, 0x00001021, 0x00641821, 0x3063fffe, 0x03e00008, 0xa7630010,
+       0x27450100, 0x8f640048, 0x8ca30018, 0x00641023, 0x18400021, 0x00000000,
+       0xaf630048, 0x8f620040, 0x9763003c, 0x00821023, 0x0043102a, 0x1040001a,
+       0x3c029000, 0x8ca40000, 0x34420001, 0x3c038000, 0x00821025, 0xaf420020,
+       0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x3c038000, 0x9362007d,
+       0x34630001, 0x3c058000, 0x00831825, 0x34420004, 0xa362007d, 0xaf430020,
+       0x8f4201f8, 0x00451024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf4401c0,
+       0xa34201c4, 0xaf4301f8, 0x03e00008, 0x00001021, 0x8f420100, 0x34420001,
+       0xaf4200a4, 0x03e00008, 0x00001021, 0x27bdffe0, 0xafbf0018, 0xafb10014,
+       0xafb00010, 0x9362007e, 0x30d000ff, 0x16020029, 0x00808821, 0x93620080,
+       0x16020026, 0x00000000, 0x9362007f, 0x16020023, 0x00000000, 0x9362007a,
+       0x16020004, 0x00000000, 0x0000000d, 0x00000000, 0x24000771, 0x0e000f49,
+       0x00000000, 0x3c039000, 0x34630001, 0x3c048000, 0x02231825, 0xa370007a,
+       0xaf430020, 0x8f420020, 0x00441024, 0x1440fffd, 0x3c028000, 0x9363007d,
+       0x34420001, 0x3c048000, 0x02221025, 0xa363007d, 0xaf420020, 0x8f4201f8,
+       0x00441024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf5101c0, 0xa34201c4,
+       0xaf4301f8, 0x0a000c79, 0x8fbf0018, 0x0000000d, 0x00000000, 0x24000781,
+       0x8fbf0018, 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x3c020800,
+       0x8c430020, 0x27bdffe8, 0xafb00010, 0x27500100, 0x10600024, 0xafbf0014,
+       0x0e001006, 0x00000000, 0x8f830018, 0x8e020000, 0xac620000, 0x8f840018,
+       0x8e020004, 0xac820004, 0x8f830018, 0x8e020018, 0xac620008, 0x8f840018,
+       0x8e03001c, 0xac83000c, 0x9602000c, 0x9203000a, 0x8f840018, 0x00021400,
+       0x00431025, 0xac820010, 0x8f830018, 0x3c026000, 0xac600014, 0x8f840018,
+       0x8c434448, 0xac830018, 0x96020008, 0x3c030800, 0x9464466e, 0x8f850018,
+       0x00021400, 0x00441025, 0x24040001, 0x0e001044, 0xaca2001c, 0x8fbf0014,
+       0x8fb00010, 0x03e00008, 0x27bd0018, 0x3c020800, 0x8c430020, 0x27bdffe8,
+       0xafb00010, 0x27500100, 0x10600020, 0xafbf0014, 0x0e001006, 0x00000000,
+       0x8f820018, 0xac400000, 0x8f830018, 0xac600004, 0x8f820018, 0xac400008,
+       0x8f830018, 0xac60000c, 0x9602000c, 0x9603000e, 0x8f840018, 0x00021400,
+       0x00431025, 0xac820010, 0x8f830018, 0x3c026000, 0xac600014, 0x8f840018,
+       0x8c434448, 0xac830018, 0x96020008, 0x3c030800, 0x9464466e, 0x8f850018,
+       0x00021400, 0x00441025, 0x24040001, 0x0e001044, 0xaca2001c, 0x8fbf0014,
+       0x8fb00010, 0x03e00008, 0x27bd0018, 0x27bdffe8, 0xafb00010, 0x27500100,
+       0xafbf0014, 0x9602000c, 0x10400024, 0x00802821, 0x3c020800, 0x8c430020,
+       0x1060003a, 0x8fbf0014, 0x0e001006, 0x00000000, 0x8f840018, 0x8e030000,
+       0xac830000, 0x9602000c, 0x8f840018, 0x00021400, 0xac820004, 0x8f830018,
+       0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, 0x8f820018,
+       0xac400014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800,
+       0xaca30018, 0x9443466e, 0x8f850018, 0x3c02400b, 0x00621825, 0x0e001044,
+       0xaca3001c, 0x0a000d19, 0x8fbf0014, 0x93620005, 0x30420010, 0x14400015,
+       0x3c029000, 0x34420001, 0x00a21025, 0xaf420020, 0x3c038000, 0x8f420020,
+       0x00431024, 0x1440fffd, 0x00000000, 0x3c038000, 0x93620005, 0x34630001,
+       0x00a02021, 0x00a31825, 0x24055852, 0x34420010, 0xa3620005, 0x0e000553,
+       0xaf430020, 0x0a000d19, 0x8fbf0014, 0x0000000d, 0x8fbf0014, 0x8fb00010,
+       0x03e00008, 0x27bd0018, 0x3c020800, 0x8c430020, 0x27bdffe8, 0xafb00010,
+       0x27500100, 0x10600022, 0xafbf0014, 0x0e001006, 0x00000000, 0x8f840018,
+       0x8e020004, 0xac820000, 0x9603000c, 0x9762002c, 0x8f840018, 0x00031c00,
+       0x00431025, 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c,
+       0x8f830018, 0xac600010, 0x8f820018, 0xac400014, 0x8f850018, 0x3c026000,
+       0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x9443466e, 0x8f850018,
+       0x3c02400e, 0x00621825, 0x0e001044, 0xaca3001c, 0x0e000d48, 0x8e040000,
+       0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x3c038000, 0x8f420278,
+       0x00431024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf440240, 0xa3420244,
+       0x03e00008, 0xaf430278, 0x3c020800, 0x8c430020, 0x27bdffe0, 0xafb10014,
+       0x00808821, 0xafb20018, 0x00c09021, 0xafb00010, 0x30b0ffff, 0x1060001c,
+       0xafbf001c, 0x0e001006, 0x00000000, 0x8f820018, 0xac510000, 0x8f840018,
+       0x00101400, 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c,
+       0x8f830018, 0xac600010, 0x8f820018, 0xac520014, 0x8f840018, 0x3c026000,
+       0x8c434448, 0x3c020800, 0xac830018, 0x9443466e, 0x8f840018, 0x3c024019,
+       0x00621825, 0xac83001c, 0x0e001044, 0x24040001, 0x8fbf001c, 0x8fb20018,
+       0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x27bdffe8, 0x27450100,
+       0xafbf0010, 0x94a3000c, 0x240200c1, 0x14620029, 0x00803021, 0x3c029000,
+       0x34420001, 0x00c21025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024,
+       0x1440fffd, 0x3c028000, 0x34420001, 0x3c049000, 0x34840001, 0x3c058000,
+       0x24030012, 0x00c21025, 0x00c42025, 0xa363003f, 0xaf420020, 0xaf440020,
+       0x8f420020, 0x00451024, 0x1440fffd, 0x3c038000, 0x9362007d, 0x34630001,
+       0x3c048000, 0x00c31825, 0x34420020, 0xa362007d, 0xaf430020, 0x8f4201f8,
+       0x00441024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf4601c0, 0xa34201c4,
+       0xaf4301f8, 0x0a000db3, 0x8fbf0010, 0x00c02021, 0x94a5000c, 0x24060001,
+       0x0e000f78, 0x240706d8, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x3c020800,
+       0x8c430020, 0x27bdffe0, 0xafb00010, 0x00808021, 0xafb20018, 0x00a09021,
+       0xafb10014, 0x30d100ff, 0x1060001c, 0xafbf001c, 0x0e001006, 0x00000000,
+       0x8f820018, 0xac500000, 0x8f840018, 0x24020001, 0xac820004, 0x8f830018,
+       0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, 0x8f820018,
+       0xac520014, 0x8f840018, 0x3c026000, 0x8c434448, 0x3c020800, 0xac830018,
+       0x9443466e, 0x8f840018, 0x3c024010, 0x00621825, 0xac83001c, 0x0e001044,
+       0x02202021, 0x8fbf001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x03e00008,
+       0x27bd0020, 0x27bdffe8, 0xafbf0014, 0xafb00010, 0x93620005, 0x30420001,
+       0x10400033, 0x00808021, 0x3c029000, 0x34420001, 0x02021025, 0xaf420020,
+       0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93620005,
+       0x3c048000, 0x3c030800, 0x304200fe, 0xa3620005, 0x8c620020, 0x34840001,
+       0x02042025, 0xaf440020, 0x10400020, 0x8fbf0014, 0x0e001006, 0x00000000,
+       0x8f820018, 0xac500000, 0x93630082, 0x9362003f, 0x8f840018, 0x00031a00,
+       0x00431025, 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c,
+       0x8f830018, 0xac600010, 0x8f820018, 0xac400014, 0x8f840018, 0x3c026000,
+       0x8c434448, 0x3c020800, 0xac830018, 0x9443466e, 0x8f840018, 0x3c02400a,
+       0x00621825, 0xac83001c, 0x0e001044, 0x24040001, 0x8fbf0014, 0x8fb00010,
+       0x03e00008, 0x27bd0018, 0x27bdffe8, 0xafbf0010, 0x8f420188, 0x00803021,
+       0x9364003f, 0x24030012, 0x00021402, 0x1483001c, 0x304500ff, 0x3c029000,
+       0x34420001, 0x3c038000, 0x00c21025, 0xa3650080, 0xa365007a, 0xaf420020,
+       0x8f420020, 0x00431024, 0x1440fffd, 0x3c028000, 0x9363007d, 0x34420001,
+       0x3c048000, 0x00c21025, 0xa363007d, 0xaf420020, 0x8f4201f8, 0x00441024,
+       0x1440fffd, 0x24020002, 0x3c031000, 0xaf4601c0, 0xa34201c4, 0xaf4301f8,
+       0x0a000e54, 0x8fbf0010, 0x9362007e, 0x1445000e, 0x00000000, 0x93620080,
+       0x1045000b, 0x00000000, 0xa3650080, 0x8f820000, 0x93660080, 0x8f440180,
+       0x8f65004c, 0x8c430000, 0x0060f809, 0x00000000, 0x0a000e54, 0x8fbf0010,
+       0xa3650080, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x3c020800, 0x8c430020,
+       0x27bdffe0, 0xafb10014, 0x00808821, 0xafb20018, 0x00a09021, 0xafb00010,
+       0x30d000ff, 0x1060002f, 0xafbf001c, 0x0e001006, 0x00000000, 0x8f820018,
+       0xac510000, 0x8f830018, 0xac700004, 0x8f820018, 0xac520008, 0x8f830018,
+       0xac60000c, 0x8f820018, 0xac400010, 0x9763006a, 0x00032880, 0x50a00001,
+       0x24050001, 0x97630068, 0x93640081, 0x3c020800, 0x8c46004c, 0x00652821,
+       0x00852804, 0x00c5102b, 0x54400001, 0x00a03021, 0x3c020800, 0x8c440050,
+       0x00c4182b, 0x54600001, 0x00c02021, 0x8f830018, 0x2402fffe, 0x00822824,
+       0x3c026000, 0xac650014, 0x8f840018, 0x8c434448, 0x3c020800, 0xac830018,
+       0x9443466e, 0x8f840018, 0x3c024011, 0x00621825, 0xac83001c, 0x0e001044,
+       0x24040001, 0x8fbf001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x03e00008,
+       0x27bd0020, 0x27bdffe8, 0xafbf0014, 0xafb00010, 0x8f440100, 0x27500100,
+       0x8f650050, 0x0e000c45, 0x9206001b, 0x3c020800, 0x8c430020, 0x1060001d,
+       0x8e100018, 0x0e001006, 0x00000000, 0x8f840018, 0x8f420100, 0xac820000,
+       0x8f830018, 0xac700004, 0x8f840018, 0x8f620050, 0xac820008, 0x8f830018,
+       0xac60000c, 0x8f820018, 0xac400010, 0x8f830018, 0x3c026000, 0xac600014,
+       0x8f850018, 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x9443466e,
+       0x8f850018, 0x3c02401c, 0x00621825, 0x0e001044, 0xaca3001c, 0x8fbf0014,
+       0x8fb00010, 0x03e00008, 0x27bd0018, 0x3c029000, 0x8f460140, 0x34420001,
+       0x3c038000, 0x00c21025, 0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd,
+       0x3c048000, 0x34840001, 0x3c059000, 0x34a50001, 0x3c078000, 0x24020012,
+       0x24030080, 0x00c42025, 0x00c52825, 0xa362003f, 0xa3630082, 0xaf440020,
+       0xaf450020, 0x8f420020, 0x00471024, 0x1440fffd, 0x3c038000, 0x9362007d,
+       0x34630001, 0x3c048000, 0x00c31825, 0x34420020, 0xa362007d, 0xaf430020,
+       0x8f4201f8, 0x00441024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf4601c0,
+       0xa34201c4, 0x03e00008, 0xaf4301f8, 0x8f430238, 0x3c020800, 0x04610013,
+       0x8c44009c, 0x2406fffe, 0x3c050800, 0x3c038000, 0x2484ffff, 0x14800009,
+       0x00000000, 0x97420078, 0x8ca3007c, 0x24420001, 0x00461024, 0x24630001,
+       0xa7620010, 0x03e00008, 0xaca3007c, 0x8f420238, 0x00431024, 0x1440fff3,
+       0x2484ffff, 0x8f420140, 0x3c031000, 0xaf420200, 0x03e00008, 0xaf430238,
+       0x3c029000, 0x8f440140, 0x34420001, 0x3c038000, 0x00821025, 0xaf420020,
+       0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x3c038000, 0x9362007d,
+       0x34630001, 0x3c058000, 0x00831825, 0x34420001, 0xa362007d, 0xaf430020,
+       0x8f4201f8, 0x00451024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf4401c0,
+       0xa34201c4, 0x03e00008, 0xaf4301f8, 0x0000000d, 0x03e00008, 0x00000000,
+       0x0000000d, 0x03e00008, 0x00000000, 0x24020001, 0x03e00008, 0xa7620010,
+       0x9362003f, 0x304400ff, 0x3883000e, 0x2c630001, 0x38820010, 0x2c420001,
+       0x00621825, 0x14600003, 0x24020012, 0x14820003, 0x00000000, 0x03e00008,
+       0x00001021, 0x9363007e, 0x9362007a, 0x14620006, 0x00000000, 0x9363007e,
+       0x24020001, 0x24630001, 0x03e00008, 0xa363007e, 0x9363007e, 0x93620080,
+       0x14620004, 0x24020001, 0xa362000b, 0x03e00008, 0x24020001, 0x03e00008,
+       0x00001021, 0x9362000b, 0x10400021, 0x00001021, 0xa360000b, 0x9362003f,
+       0x304400ff, 0x3883000e, 0x2c630001, 0x38820010, 0x2c420001, 0x00621825,
+       0x14600015, 0x00001821, 0x24020012, 0x10820012, 0x00000000, 0x9363007e,
+       0x9362007a, 0x14620007, 0x00000000, 0x9362007e, 0x24030001, 0x24420001,
+       0xa362007e, 0x03e00008, 0x00601021, 0x9363007e, 0x93620080, 0x14620004,
+       0x00001821, 0x24020001, 0xa362000b, 0x24030001, 0x03e00008, 0x00601021,
+       0x03e00008, 0x00000000, 0x24040001, 0xaf64000c, 0x8f6300dc, 0x8f6200cc,
+       0x50620001, 0xa7640010, 0xa7640012, 0xa7640014, 0x03e00008, 0xa7640016,
+       0x27bdffd8, 0xafb00010, 0x00808021, 0xafb3001c, 0x00c09821, 0xafbf0020,
+       0xafb20018, 0xafb10014, 0x93620023, 0x00e09021, 0x30420040, 0x10400020,
+       0x30b1ffff, 0x3c020800, 0x8c430020, 0x1060001c, 0x00000000, 0x0e001006,
+       0x00000000, 0x8f820018, 0xac500000, 0x8f840018, 0x3c02008d, 0xac820004,
+       0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010,
+       0x8f820018, 0xac520014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001,
+       0x3c020800, 0xaca30018, 0x9443466e, 0x8f850018, 0x3c024019, 0x00621825,
+       0x0e001044, 0xaca3001c, 0x93620023, 0x30420020, 0x14400003, 0x3c020800,
+       0x52600020, 0x3c029000, 0x8c430020, 0x1060001d, 0x3c029000, 0x0e001006,
+       0x00000000, 0x8f820018, 0xac500000, 0x8f840018, 0x00111400, 0xac820004,
+       0x8f830018, 0xac720008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010,
+       0x8f820018, 0xac400014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001,
+       0x3c020800, 0xaca30018, 0x9443466e, 0x8f850018, 0x3c02401b, 0x00621825,
+       0x0e001044, 0xaca3001c, 0x3c029000, 0x34420001, 0x02021025, 0xaf420020,
+       0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93630023,
+       0x3c028000, 0x34420001, 0x02021025, 0x8fbf0020, 0x8fb3001c, 0x8fb20018,
+       0x8fb10014, 0x8fb00010, 0x3063009f, 0xa3630023, 0xaf420020, 0x03e00008,
+       0x27bd0028, 0x3c020800, 0x8c430020, 0x27bdffe8, 0xafb00010, 0x27500100,
+       0x1060001d, 0xafbf0014, 0x0e001006, 0x00000000, 0x8f830018, 0x8e020004,
+       0xac620000, 0x8f840018, 0x8e020018, 0xac820004, 0x8f850018, 0x8e020000,
+       0xaca20008, 0x8f830018, 0xac60000c, 0x8f820018, 0xac400010, 0x8f830018,
+       0xac600014, 0x8f820018, 0xac400018, 0x96030008, 0x3c020800, 0x9444466e,
+       0x8f850018, 0x00031c00, 0x00641825, 0x24040001, 0x0e001044, 0xaca3001c,
+       0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x3c060800, 0x24c54660,
+       0x3c02000a, 0x03421821, 0x94640006, 0x94a2000a, 0x00441023, 0x00021400,
+       0x00021c03, 0x04610006, 0xa4a40006, 0x0000000d, 0x00000000, 0x2400005a,
+       0x0a00101b, 0x24020001, 0x8f820014, 0x0062102b, 0x14400002, 0x00001021,
+       0x24020001, 0x304200ff, 0x1040001c, 0x274a0400, 0x3c07000a, 0x3c020800,
+       0x24454660, 0x94a9000a, 0x8f880014, 0x03471021, 0x94430006, 0x00402021,
+       0xa4a30006, 0x94820006, 0xa4a20006, 0x01221023, 0x00021400, 0x00021403,
+       0x04410006, 0x0048102b, 0x0000000d, 0x00000000, 0x2400005a, 0x0a001036,
+       0x24020001, 0x14400002, 0x00001021, 0x24020001, 0x304200ff, 0x1440ffec,
+       0x03471021, 0x24c44660, 0x8c820010, 0xaf420038, 0x8c830014, 0x3c020005,
+       0xaf43003c, 0xaf420030, 0xaf800010, 0xaf8a0018, 0x03e00008, 0x00000000,
+       0x27bdffe0, 0x8f820010, 0x8f850018, 0x3c070800, 0x24e84660, 0xafbf001c,
+       0xafb20018, 0xafb10014, 0xafb00010, 0x9503000a, 0x8d060014, 0x00009021,
+       0x309000ff, 0x00e08821, 0x24420001, 0x24a50020, 0x24630001, 0xaf820010,
+       0xaf850018, 0xa503000a, 0x24c30020, 0x3c028000, 0x04c10007, 0xad030014,
+       0x00621024, 0x14400005, 0x26224660, 0x8d020010, 0x24420001, 0xad020010,
+       0x26224660, 0x9444000a, 0x94450018, 0x0010102b, 0x00a41826, 0x2c630001,
+       0x00621825, 0x1060001c, 0x3c030006, 0x8f820010, 0x24120001, 0x00021140,
+       0x00431025, 0xaf420030, 0x00000000, 0x00000000, 0x00000000, 0x27450400,
+       0x8f420000, 0x30420010, 0x1040fffd, 0x26224660, 0x9444000a, 0x94430018,
+       0xaf800010, 0xaf850018, 0x14830012, 0x26274660, 0x0e0010d2, 0x00000000,
+       0x1600000e, 0x26274660, 0x0e001006, 0x00000000, 0x0a00108f, 0x26274660,
+       0x00041c00, 0x00031c03, 0x00051400, 0x00021403, 0x00621823, 0x18600002,
+       0x3c026000, 0xac400808, 0x26274660, 0x94e2000e, 0x94e3000c, 0x24420001,
+       0xa4e2000e, 0x3042ffff, 0x50430001, 0xa4e0000e, 0x12000005, 0x3c02000a,
+       0x94e2000a, 0xa74200a2, 0x0a0010cc, 0x02401021, 0x03421821, 0x94640006,
+       0x94e2000a, 0x00441023, 0x00021400, 0x00021c03, 0x04610006, 0xa4e40006,
+       0x0000000d, 0x00000000, 0x2400005a, 0x0a0010ae, 0x24020001, 0x8f820014,
+       0x0062102b, 0x14400002, 0x00001021, 0x24020001, 0x304200ff, 0x1040001b,
+       0x3c020800, 0x3c06000a, 0x24454660, 0x94a8000a, 0x8f870014, 0x03461021,
+       0x94430006, 0x00402021, 0xa4a30006, 0x94820006, 0xa4a20006, 0x01021023,
+       0x00021400, 0x00021403, 0x04410006, 0x0047102b, 0x0000000d, 0x00000000,
+       0x2400005a, 0x0a0010c8, 0x24020001, 0x14400002, 0x00001021, 0x24020001,
+       0x304200ff, 0x1440ffec, 0x03461021, 0x02401021, 0x8fbf001c, 0x8fb20018,
+       0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x3c020800, 0x24454660,
+       0x94a3001a, 0x8ca40024, 0x00403021, 0x000318c0, 0x00832021, 0xaf44003c,
+       0x8ca20020, 0xaf420038, 0x3c020050, 0x34420008, 0xaf420030, 0x00000000,
+       0x00000000, 0x00000000, 0x8f420000, 0x30420020, 0x1040fffd, 0x00000000,
+       0x8f430400, 0x24c64660, 0xacc30010, 0x8f420404, 0x3c030020, 0xacc20014,
+       0xaf430030, 0x94c40018, 0x94c3001c, 0x94c2001a, 0x94c5001e, 0x00832021,
+       0x24420001, 0xa4c2001a, 0x3042ffff, 0x14450002, 0xa4c40018, 0xa4c0001a,
+       0x03e00008, 0x00000000, 0x8f820010, 0x3c030006, 0x00021140, 0x00431025,
+       0xaf420030, 0x00000000, 0x00000000, 0x00000000, 0x27430400, 0x8f420000,
+       0x30420010, 0x1040fffd, 0x00000000, 0xaf800010, 0xaf830018, 0x03e00008,
+       0x00000000, 0x27bdffe8, 0xafb00010, 0x3c100800, 0x26104660, 0x3c05000a,
+       0x02002021, 0x03452821, 0xafbf0014, 0x0e001128, 0x2406000a, 0x96020002,
+       0x9603001e, 0x3042000f, 0x24420003, 0x00431804, 0x24027fff, 0x0043102b,
+       0xaf830014, 0x10400004, 0x00000000, 0x0000000d, 0x00000000, 0x24000043,
+       0x0e0010d2, 0x00000000, 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018,
+       0x10c00007, 0x00000000, 0x8ca20000, 0x24c6ffff, 0x24a50004, 0xac820000,
+       0x14c0fffb, 0x24840004, 0x03e00008, 0x00000000, 0x0a001137, 0x00a01021,
+       0xac860000, 0x24840004, 0x00a01021, 0x1440fffc, 0x24a5ffff, 0x03e00008,
+       0x00000000, 0x3c036000, 0x8c642b7c, 0x3c036010, 0x8c6553fc, 0x00041582,
+       0x00042302, 0x308403ff, 0x00052d82, 0x00441026, 0x0002102b, 0x0005282b,
+       0x00451025, 0x1440000d, 0x3c020050, 0x34420004, 0xaf400038, 0xaf40003c,
+       0xaf420030, 0x00000000, 0x00000000, 0x8f420000, 0x30420020, 0x1040fffd,
+       0x3c020020, 0xaf420030, 0x0000000d, 0x03e00008, 0x00000000, 0x3c020050,
+       0x34420004, 0xaf440038, 0xaf45003c, 0xaf420030, 0x00000000, 0x00000000,
+       0x8f420000, 0x30420020, 0x1040fffd, 0x3c020020, 0xaf420030, 0x03e00008,
+       0x00000000, 0x00000000 };
+
+static u32 bnx2_COM_b06FwData[(0x0/4) + 1] = { 0x00000000 };
+static u32 bnx2_COM_b06FwRodata[(0x18/4) + 1] = {
+       0x08002318, 0x08002348, 0x08002378, 0x080023a8, 0x080023d8, 0x00000000,
+       0x00000000 };
+
+static u32 bnx2_COM_b06FwBss[(0x88/4) + 1] = { 0x00000000 };
+static u32 bnx2_COM_b06FwSbss[(0x1c/4) + 1] = { 0x00000000 };
+
+static int bnx2_RXP_b06FwReleaseMajor = 0x0;
+static int bnx2_RXP_b06FwReleaseMinor = 0x0;
+static int bnx2_RXP_b06FwReleaseFix = 0x0;
+static u32 bnx2_RXP_b06FwStartAddr = 0x08000060;
+static u32 bnx2_RXP_b06FwTextAddr = 0x08000000;
+static int bnx2_RXP_b06FwTextLen = 0x20b8;
+static u32 bnx2_RXP_b06FwDataAddr = 0x080020e0;
+static int bnx2_RXP_b06FwDataLen = 0x0;
+static u32 bnx2_RXP_b06FwRodataAddr = 0x00000000;
+static int bnx2_RXP_b06FwRodataLen = 0x0;
+static u32 bnx2_RXP_b06FwBssAddr = 0x08002100;
+static int bnx2_RXP_b06FwBssLen = 0x239c;
+static u32 bnx2_RXP_b06FwSbssAddr = 0x080020e0;
+static int bnx2_RXP_b06FwSbssLen = 0x14;
+
+static u32 bnx2_RXP_b06FwText[(0x20b8/4) + 1] = {
+       0x0a000018, 0x00000000, 0x00000000, 0x0000000d, 0x72787020, 0x302e362e,
+       0x39000000, 0x00060903, 0x00000000, 0x0000000d, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x10000003, 0x00000000, 0x0000000d, 0x0000000d, 0x3c020800,
+       0x244220e0, 0x3c030800, 0x2463449c, 0xac400000, 0x0043202b, 0x1480fffd,
+       0x24420004, 0x3c1d0800, 0x37bd3ffc, 0x03a0f021, 0x3c100800, 0x26100060,
+       0x3c1c0800, 0x279c20e0, 0x0e000329, 0x00000000, 0x0000000d, 0x8f870008,
+       0x2ce20080, 0x10400018, 0x3c030800, 0x24633490, 0x8f460100, 0x00072140,
+       0x00831021, 0xac460000, 0x8f450104, 0x00641021, 0xac450004, 0x8f460108,
+       0xac460008, 0x8f45010c, 0xac45000c, 0x8f460114, 0xac460010, 0x8f450118,
+       0xac450014, 0x8f460124, 0xac460018, 0x8f450128, 0x00641821, 0x24e20001,
+       0xaf820008, 0xac65001c, 0x03e00008, 0x00000000, 0x00804021, 0x8f830000,
+       0x24070001, 0x3c020001, 0x00621024, 0x10400037, 0x00603021, 0x9742010e,
+       0x3c038000, 0x3045ffff, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003,
+       0xa342018b, 0x8f840004, 0x24020080, 0x24030002, 0xaf420180, 0xa743018c,
+       0x10800005, 0xa745018e, 0x9743011c, 0x9742011e, 0x0a000069, 0x00021400,
+       0x9743011e, 0x9742011c, 0x00021400, 0x00621825, 0xaf4301a8, 0x8f84000c,
+       0x24020003, 0x30838000, 0x1060000d, 0xa7420188, 0x93420116, 0x304200fc,
+       0x005a1021, 0x24424004, 0x8c430000, 0x3063ffff, 0x14600005, 0x00000000,
+       0x3c02ffff, 0x34427fff, 0x00821024, 0xaf82000c, 0x9782000e, 0x9743010c,
+       0x8f440104, 0x3042bfff, 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6,
+       0xaf4301ac, 0x3c021000, 0xaf4201b8, 0x03e00008, 0x00001021, 0x30c21000,
+       0x1040000f, 0x00000000, 0x9742010c, 0x3042fc00, 0x5440000b, 0x24070005,
+       0x3c021000, 0x00c21024, 0x10400007, 0x3c030dff, 0x3463ffff, 0x3c020e00,
+       0x00c21024, 0x0062182b, 0x54600001, 0x24070005, 0x8f82000c, 0x30434000,
+       0x10600016, 0x00404821, 0x3c020f00, 0x00c21024, 0x14400012, 0x00000000,
+       0x93420116, 0x34424000, 0x03421821, 0x94650002, 0x2ca21389, 0x1040000b,
+       0x3c020800, 0x24422100, 0x00051942, 0x00031880, 0x00621821, 0x30a5001f,
+       0x8c640000, 0x24020001, 0x00a21004, 0x00822024, 0x01044025, 0x11000037,
+       0x3c021000, 0x9742010e, 0x34e60002, 0x3c038000, 0x24420004, 0x3045ffff,
+       0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, 0xa342018b, 0x8f840004,
+       0x24020180, 0x24030002, 0xaf420180, 0xa743018c, 0x10800005, 0xa745018e,
+       0x9743011c, 0x9742011e, 0x0a0000cd, 0x00021400, 0x9743011e, 0x9742011c,
+       0x00021400, 0x00621825, 0xaf4301a8, 0x8f84000c, 0x30828000, 0x1040000c,
+       0xa7460188, 0x93420116, 0x304200fc, 0x005a1021, 0x24424004, 0x8c430000,
+       0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff, 0x00821024, 0xaf82000c,
+       0x9782000e, 0x9743010c, 0x8f440104, 0x3042bfff, 0x00031c00, 0x3084ffff,
+       0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000, 0xaf4201b8, 0x03e00008,
+       0x00001021, 0x00c21024, 0x104000ba, 0x3c020800, 0x8c430030, 0x1060003e,
+       0x31224000, 0x1040003c, 0x3c030f00, 0x00c31824, 0x3c020100, 0x0043102b,
+       0x14400038, 0x3c030800, 0x9742010e, 0x34e60002, 0x3c038000, 0x24420004,
+       0x3045ffff, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, 0xa342018b,
+       0x8f840004, 0x24020080, 0x24030002, 0xaf420180, 0xa743018c, 0x10800005,
+       0xa745018e, 0x9743011c, 0x9742011e, 0x0a000110, 0x00021400, 0x9743011e,
+       0x9742011c, 0x00021400, 0x00621825, 0xaf4301a8, 0x8f84000c, 0x30828000,
+       0x1040000c, 0xa7460188, 0x93420116, 0x304200fc, 0x005a1021, 0x24424004,
+       0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff, 0x00821024,
+       0xaf82000c, 0x9782000e, 0x9743010c, 0x8f440104, 0x3042bfff, 0x00031c00,
+       0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000, 0xaf4201b8,
+       0x03e00008, 0x00001021, 0x3c030800, 0x8c620024, 0x30420008, 0x1040003d,
+       0x34e80002, 0x3c020f00, 0x00c21024, 0x5440003a, 0x3107ffff, 0x9742010c,
+       0x30420200, 0x50400036, 0x3107ffff, 0x9742010e, 0x30e6fffb, 0x3c038000,
+       0x24420004, 0x3045ffff, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003,
+       0xa342018b, 0x8f840004, 0x24020180, 0x24030002, 0xaf420180, 0xa743018c,
+       0x10800005, 0xa745018e, 0x9743011c, 0x9742011e, 0x0a000153, 0x00021400,
+       0x9743011e, 0x9742011c, 0x00021400, 0x00621825, 0xaf4301a8, 0x8f84000c,
+       0x30828000, 0x1040000c, 0xa7460188, 0x93420116, 0x304200fc, 0x005a1021,
+       0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff,
+       0x00821024, 0xaf82000c, 0x9782000e, 0x9743010c, 0x8f440104, 0x3042bfff,
+       0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000,
+       0xaf4201b8, 0x3107ffff, 0x8f820000, 0x3c068000, 0x9743010e, 0x00021442,
+       0x30440780, 0x24630004, 0x3065ffff, 0x8f4201b8, 0x00461024, 0x1440fffd,
+       0x24020003, 0xa342018b, 0x8f830004, 0x24020002, 0xaf440180, 0xa742018c,
+       0x10600005, 0xa745018e, 0x9743011c, 0x9742011e, 0x0a000189, 0x00021400,
+       0x9743011e, 0x9742011c, 0x00021400, 0x00621825, 0xaf4301a8, 0x8f84000c,
+       0x30828000, 0x1040000c, 0xa7470188, 0x93420116, 0x304200fc, 0x005a1021,
+       0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff,
+       0x00821024, 0xaf82000c, 0x9782000e, 0x9743010c, 0x8f440104, 0x3042bfff,
+       0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000,
+       0xaf4201b8, 0x03e00008, 0x00001021, 0x8f424000, 0x30420100, 0x104000ef,
+       0x3c020800, 0x8c440024, 0x24030001, 0x14830036, 0x00404021, 0x9742010e,
+       0x34e50002, 0x3c038000, 0x24420004, 0x3044ffff, 0x8f4201b8, 0x00431024,
+       0x1440fffd, 0x24020003, 0xa342018b, 0x8f830004, 0x24020002, 0xaf400180,
+       0xa742018c, 0x10600005, 0xa744018e, 0x9743011c, 0x9742011e, 0x0a0001c6,
+       0x00021400, 0x9743011e, 0x9742011c, 0x00021400, 0x00621825, 0xaf4301a8,
+       0x8f84000c, 0x30828000, 0x1040000c, 0xa7450188, 0x93420116, 0x304200fc,
+       0x005a1021, 0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff,
+       0x34427fff, 0x00821024, 0xaf82000c, 0x9782000e, 0x9743010c, 0x8f440104,
+       0x3042bfff, 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac,
+       0x3c021000, 0xaf4201b8, 0x03e00008, 0x00001021, 0x30820001, 0x10400035,
+       0x30e90004, 0x9742010e, 0x30e6fffb, 0x3c038000, 0x24420004, 0x3044ffff,
+       0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, 0xa342018b, 0x8f830004,
+       0x24020002, 0xaf400180, 0xa742018c, 0x10600005, 0xa744018e, 0x9743011c,
+       0x9742011e, 0x0a0001fe, 0x00021400, 0x9743011e, 0x9742011c, 0x00021400,
+       0x00621825, 0xaf4301a8, 0x8f84000c, 0x30828000, 0x1040000c, 0xa7470188,
+       0x93420116, 0x304200fc, 0x005a1021, 0x24424004, 0x8c430000, 0x3063ffff,
+       0x14600004, 0x3c02ffff, 0x34427fff, 0x00821024, 0xaf82000c, 0x9782000e,
+       0x9743010c, 0x8f440104, 0x3042bfff, 0x00031c00, 0x3084ffff, 0x00641825,
+       0xa74201a6, 0xaf4301ac, 0x3c021000, 0xaf4201b8, 0x30c7ffff, 0x8d020024,
+       0x30420004, 0x10400037, 0x8d020024, 0x9742010e, 0x30e6fffb, 0x3c038000,
+       0x24420004, 0x3045ffff, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003,
+       0xa342018b, 0x8f840004, 0x24020100, 0x24030002, 0xaf420180, 0xa743018c,
+       0x10800005, 0xa745018e, 0x9743011c, 0x9742011e, 0x0a000237, 0x00021400,
+       0x9743011e, 0x9742011c, 0x00021400, 0x00621825, 0xaf4301a8, 0x8f84000c,
+       0x30828000, 0x1040000c, 0xa7470188, 0x93420116, 0x304200fc, 0x005a1021,
+       0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff,
+       0x00821024, 0xaf82000c, 0x9782000e, 0x9743010c, 0x8f440104, 0x3042bfff,
+       0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000,
+       0xaf4201b8, 0x30c7ffff, 0x8d020024, 0x30420008, 0x10400034, 0x00000000,
+       0x9742010e, 0x3c038000, 0x24420004, 0x3045ffff, 0x8f4201b8, 0x00431024,
+       0x1440fffd, 0x24020003, 0xa342018b, 0x8f840004, 0x24020180, 0x24030002,
+       0xaf420180, 0xa743018c, 0x10800005, 0xa745018e, 0x9743011c, 0x9742011e,
+       0x0a00026f, 0x00021400, 0x9743011e, 0x9742011c, 0x00021400, 0x00621825,
+       0xaf4301a8, 0x8f84000c, 0x30828000, 0x1040000c, 0xa7470188, 0x93420116,
+       0x304200fc, 0x005a1021, 0x24424004, 0x8c430000, 0x3063ffff, 0x14600004,
+       0x3c02ffff, 0x34427fff, 0x00821024, 0xaf82000c, 0x9782000e, 0x9743010c,
+       0x8f440104, 0x3042bfff, 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6,
+       0xaf4301ac, 0x3c021000, 0xaf4201b8, 0x15200046, 0x00001021, 0x3c038000,
+       0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020002, 0x24032000, 0xa342018b,
+       0xa7430188, 0x3c021000, 0xaf4201b8, 0x03e00008, 0x00001021, 0x3c030800,
+       0x8c620024, 0x30420001, 0x10400035, 0x00001021, 0x9742010e, 0x34e50002,
+       0x3c038000, 0x24420004, 0x3044ffff, 0x8f4201b8, 0x00431024, 0x1440fffd,
+       0x24020003, 0xa342018b, 0x8f830004, 0x24020002, 0xaf400180, 0xa742018c,
+       0x10600005, 0xa744018e, 0x9743011c, 0x9742011e, 0x0a0002b5, 0x00021400,
+       0x9743011e, 0x9742011c, 0x00021400, 0x00621825, 0xaf4301a8, 0x8f84000c,
+       0x30828000, 0x1040000c, 0xa7450188, 0x93420116, 0x304200fc, 0x005a1021,
+       0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff,
+       0x00821024, 0xaf82000c, 0x9782000e, 0x9743010c, 0x8f440104, 0x3042bfff,
+       0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000,
+       0xaf4201b8, 0x00001021, 0x03e00008, 0x00000000, 0x27bdffe0, 0xafbf0018,
+       0xafb10014, 0xafb00010, 0x8f420140, 0xaf420020, 0x8f430148, 0x3c027000,
+       0x00621824, 0x3c024000, 0x1062000c, 0x0043102b, 0x14400006, 0x3c025000,
+       0x3c023000, 0x1062000b, 0x3c024000, 0x0a00031f, 0x00000000, 0x10620034,
+       0x3c024000, 0x0a00031f, 0x00000000, 0x0e00067c, 0x00000000, 0x0a00031f,
+       0x3c024000, 0x8f420148, 0x24030002, 0x3044ffff, 0x00021402, 0x305000ff,
+       0x1203000c, 0x27510180, 0x2a020003, 0x10400005, 0x24020003, 0x0600001d,
+       0x36053000, 0x0a00030a, 0x3c038000, 0x12020007, 0x00000000, 0x0a000317,
+       0x00000000, 0x0e000423, 0x00000000, 0x0a000308, 0x00402021, 0x0e000435,
+       0x00000000, 0x00402021, 0x36053000, 0x3c038000, 0x8f4201b8, 0x00431024,
+       0x1440fffd, 0x24020002, 0xa6250008, 0xa222000b, 0xa6240010, 0x8f420144,
+       0x3c031000, 0xae220024, 0xaf4301b8, 0x0a00031f, 0x3c024000, 0x0000000d,
+       0x00000000, 0x240001c3, 0x0a00031f, 0x3c024000, 0x0e0007f7, 0x00000000,
+       0x3c024000, 0xaf420178, 0x00000000, 0x8fbf0018, 0x8fb10014, 0x8fb00010,
+       0x03e00008, 0x27bd0020, 0x24020800, 0x03e00008, 0xaf4201b8, 0x27bdffe8,
+       0x3c04600c, 0xafbf0014, 0xafb00010, 0x8c825000, 0x3c1a8000, 0x2403ff7f,
+       0x3c106000, 0x00431024, 0x3442380c, 0x24030003, 0xac825000, 0x3c040008,
+       0xaf430008, 0x8e020808, 0x3c030800, 0xac600020, 0x3042fff0, 0x2c420001,
+       0xaf820004, 0x0e000819, 0x0344d825, 0x0e000781, 0x00000000, 0x3c020400,
+       0x3442000c, 0x3c03ffff, 0x34630806, 0xae021948, 0xae03194c, 0x8e021980,
+       0x34420200, 0xae021980, 0x8f500000, 0x32020003, 0x1040fffd, 0x32020001,
+       0x10400004, 0x32020002, 0x0e0003bd, 0x00000000, 0x32020002, 0x1040fff6,
+       0x00000000, 0x0e0002d4, 0x00000000, 0x0a00034a, 0x00000000, 0x27bdffe8,
+       0x3c04600c, 0xafbf0014, 0xafb00010, 0x8c825000, 0x3c1a8000, 0x2403ff7f,
+       0x3c106000, 0x00431024, 0x3442380c, 0x24030003, 0xac825000, 0x3c040008,
+       0xaf430008, 0x8e020808, 0x3c030800, 0xac600020, 0x3042fff0, 0x2c420001,
+       0xaf820004, 0x0e000819, 0x0344d825, 0x0e000781, 0x00000000, 0x3c020400,
+       0x3442000c, 0x3c03ffff, 0x34630806, 0xae021948, 0xae03194c, 0x8e021980,
+       0x8fbf0014, 0x34420200, 0xae021980, 0x8fb00010, 0x03e00008, 0x27bd0018,
+       0x30a5ffff, 0x30c6ffff, 0x30e7ffff, 0x3c038000, 0x8f4201b8, 0x00431024,
+       0x1440fffd, 0x24020003, 0xa342018b, 0x8f830004, 0xaf440180, 0xa745018c,
+       0x10600005, 0xa746018e, 0x9743011c, 0x9742011e, 0x0a000393, 0x00021400,
+       0x9743011e, 0x9742011c, 0x00021400, 0x00621825, 0xaf4301a8, 0x8f84000c,
+       0x30828000, 0x1040000c, 0xa7470188, 0x93420116, 0x304200fc, 0x005a1021,
+       0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff,
+       0x00821024, 0xaf82000c, 0x9782000e, 0x9743010c, 0x8f440104, 0x3042bfff,
+       0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000,
+       0xaf4201b8, 0x03e00008, 0x00000000, 0x3c038000, 0x8f4201b8, 0x00431024,
+       0x1440fffd, 0x24020002, 0x24032000, 0xa342018b, 0xa7430188, 0x3c021000,
+       0xaf4201b8, 0x03e00008, 0x00000000, 0x27bdffe8, 0xafbf0010, 0x8f460128,
+       0xaf460020, 0x8f420104, 0x8f450100, 0x24030800, 0x3c040010, 0xaf820000,
+       0x00441024, 0xaf85000c, 0xaf4301b8, 0x14400005, 0x3c02001f, 0x3c030800,
+       0x8c620020, 0x0a0003d5, 0x00002021, 0x3442ff00, 0x14c20009, 0x2402bfff,
+       0x3c030800, 0x8c620020, 0x24040001, 0x24420001, 0x0e00004c, 0xac620020,
+       0x0a0003e4, 0x00000000, 0x00a21024, 0x14400006, 0x00000000, 0xaf400048,
+       0x0e000448, 0xaf400040, 0x0a0003e4, 0x00000000, 0x0e000783, 0x00000000,
+       0x10400005, 0x3c024000, 0x8f430124, 0x3c026020, 0xac430014, 0x3c024000,
+       0xaf420138, 0x00000000, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x27bdffe0,
+       0xafbf0018, 0xafb10014, 0xafb00010, 0x8f420148, 0x24030002, 0x3044ffff,
+       0x00021402, 0x305000ff, 0x1203000c, 0x27510180, 0x2a020003, 0x10400005,
+       0x24020003, 0x0600001d, 0x36053000, 0x0a00040e, 0x3c038000, 0x12020007,
+       0x00000000, 0x0a00041b, 0x00000000, 0x0e000423, 0x00000000, 0x0a00040c,
+       0x00402021, 0x0e000435, 0x00000000, 0x00402021, 0x36053000, 0x3c038000,
+       0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020002, 0xa6250008, 0xa222000b,
+       0xa6240010, 0x8f420144, 0x3c031000, 0xae220024, 0xaf4301b8, 0x0a00041f,
+       0x8fbf0018, 0x0000000d, 0x00000000, 0x240001c3, 0x8fbf0018, 0x8fb10014,
+       0x8fb00010, 0x03e00008, 0x27bd0020, 0x3084ffff, 0x2c821389, 0x1040000d,
+       0x00001021, 0x3c030800, 0x24632100, 0x00042942, 0x00052880, 0x00a32821,
+       0x3086001f, 0x8ca40000, 0x24030001, 0x00c31804, 0x00832025, 0x03e00008,
+       0xaca40000, 0x03e00008, 0x24020091, 0x3084ffff, 0x2c821389, 0x1040000e,
+       0x00001021, 0x3c030800, 0x24632100, 0x00042942, 0x00052880, 0x00a32821,
+       0x3086001f, 0x24030001, 0x8ca40000, 0x00c31804, 0x00031827, 0x00832024,
+       0x03e00008, 0xaca40000, 0x03e00008, 0x24020091, 0x27bdffb0, 0x3c026000,
+       0xafbf0048, 0x8c434448, 0xaf630140, 0x93620005, 0x30420001, 0x14400005,
+       0x00000000, 0x0e0007ed, 0x00000000, 0x0a00067a, 0x8fbf0048, 0x93420116,
+       0x93430112, 0x8f430104, 0x3c040020, 0x34424000, 0x00641824, 0x1060000d,
+       0x03426021, 0x8f430128, 0x27420180, 0xac430000, 0x8f650040, 0x24040008,
+       0x240340c1, 0xa4430008, 0x24030002, 0xa043000b, 0x3c031000, 0x0a000563,
+       0xa044000a, 0x8f420104, 0x3c030040, 0x00431024, 0x10400007, 0x00000000,
+       0x8f430128, 0x27420180, 0xac430000, 0x8f650040, 0x0a00055c, 0x24040010,
+       0xaf400048, 0xaf400054, 0xaf400040, 0x8f630048, 0x8f620040, 0x00624823,
+       0x05210004, 0x00000000, 0x0000000d, 0x00000000, 0x24000132, 0x9742011a,
+       0x3046ffff, 0x10c00004, 0x8d880004, 0x01061021, 0x0a000487, 0x2445ffff,
+       0x01002821, 0x918a000d, 0xa7a00020, 0xafa00028, 0x9364003f, 0x3c026000,
+       0x8c434448, 0x308700ff, 0x31420004, 0x10400033, 0xaf630144, 0x24090012,
+       0x14e90006, 0x3c040800, 0x8c830028, 0x24020001, 0x24630001, 0x0a00054e,
+       0xac830028, 0x8f620044, 0x15020012, 0x97a20020, 0x27a60010, 0x27450180,
+       0x3442001a, 0xa7a20020, 0x8f630040, 0x3c048000, 0x24020020, 0xa3a70022,
+       0xa3a90023, 0xa3a2001a, 0xafa30028, 0x8f4201b8, 0x00441024, 0x1440fffd,
+       0x00000000, 0x0a000533, 0x00000000, 0x8f620044, 0x01021023, 0x0440009e,
+       0x24020001, 0x8f620048, 0x01021023, 0x0441009a, 0x24020001, 0x97a20020,
+       0x27a60010, 0x34420001, 0xa7a20020, 0x8f630040, 0x27450180, 0x3c048000,
+       0xafa30028, 0x8f4201b8, 0x00441024, 0x1440fffd, 0x00000000, 0x0a000533,
+       0x00000000, 0x3c026000, 0x8c424448, 0xaf620148, 0x8f630040, 0x00685823,
+       0x19600013, 0x00cb102a, 0x54400007, 0x314a00fe, 0x5566000c, 0x010b4021,
+       0x31420001, 0x54400009, 0x010b4021, 0x314a00fe, 0x24020001, 0xa7a20020,
+       0x8f630040, 0x00c05821, 0x00003021, 0x0a0004dd, 0xafa30028, 0x00cb1023,
+       0x0a0004dd, 0x3046ffff, 0x00005821, 0x8f620048, 0x2442ffff, 0x00a21823,
+       0x18600019, 0x0066102a, 0x14400013, 0x24020001, 0xa7a20020, 0x8f630040,
+       0xafa30028, 0x8f620040, 0x55020005, 0x27a60010, 0x55200003, 0x27a60010,
+       0x0a0004f6, 0x00c01821, 0x27450180, 0x3c038000, 0x8f4201b8, 0x00431024,
+       0x1440fffd, 0x00000000, 0x0a000533, 0x00000000, 0x8f650048, 0x00c31023,
+       0x3046ffff, 0x314a00f6, 0x3c046000, 0x8c824448, 0x31430002, 0x1060001e,
+       0xaf62014c, 0x8f620044, 0x1502000e, 0x97a20020, 0x27a60010, 0x34420200,
+       0xa7a20020, 0x8f630040, 0x27450180, 0x3c048000, 0xafa30028, 0x8f4201b8,
+       0x00441024, 0x1440fffd, 0x00000000, 0x0a000533, 0x00000000, 0x27a60010,
+       0x34420001, 0xa7a20020, 0x8f630040, 0x27450180, 0x3c048000, 0xafa30028,
+       0x8f4201b8, 0x00441024, 0x1440fffd, 0x00000000, 0x0a000533, 0x00000000,
+       0x3c026000, 0x8c424448, 0x31430010, 0xaf620150, 0x54600003, 0x8d890008,
+       0x0a00054e, 0x24020001, 0x8f630054, 0x2522ffff, 0x00431023, 0x1840002a,
+       0x24020001, 0x27a60010, 0xa7a20020, 0x8f630040, 0x27450180, 0x3c048000,
+       0xafa30028, 0x8f4201b8, 0x00441024, 0x1440fffd, 0x00000000, 0x8f420128,
+       0xaca20000, 0x8cc30018, 0x240240c1, 0xa4a20008, 0xaca30018, 0x90c4000a,
+       0x24020002, 0xa0a2000b, 0xa0a4000a, 0x94c20010, 0xa4a20010, 0x90c30012,
+       0xa0a30012, 0x90c20013, 0xa0a20013, 0x8cc30014, 0xaca30014, 0x8cc20024,
+       0xaca20024, 0x8cc30028, 0xaca30028, 0x8cc4002c, 0x24020001, 0x3c031000,
+       0xaca4002c, 0xaf4301b8, 0xaf400044, 0xaf400050, 0x0a00067a, 0x8fbf0048,
+       0x3c026000, 0x8c424448, 0x31430020, 0x10600019, 0xaf620154, 0x8f430128,
+       0x27420180, 0xac430000, 0x8f650040, 0x24040004, 0x240340c1, 0xa4430008,
+       0x24030002, 0xa044000a, 0x24040008, 0xa043000b, 0x3c031000, 0xa4440010,
+       0xa0400012, 0xa0400013, 0xac400014, 0xac400024, 0xac400028, 0xac40002c,
+       0xac450018, 0x0e0007ed, 0xaf4301b8, 0x0a00067a, 0x8fbf0048, 0x8f430104,
+       0x8c824448, 0x38e3000a, 0x2c630001, 0xaf620158, 0x38e2000c, 0x2c420001,
+       0x00621825, 0x14600003, 0x2402000e, 0x14e2002a, 0x00000000, 0x50c00008,
+       0x9584000e, 0x10c00004, 0xa7a60040, 0x01061021, 0x0a000583, 0x2445ffff,
+       0x01002821, 0x9584000e, 0x93630035, 0x8f62004c, 0x00642004, 0x00892021,
+       0x00821023, 0x1840001f, 0x3c026000, 0x8f620018, 0x01021023, 0x1c40000f,
+       0x97a20020, 0x8f620018, 0x15020018, 0x3c026000, 0x8f62001c, 0x01221023,
+       0x1c400008, 0x97a20020, 0x8f62001c, 0x15220011, 0x3c026000, 0x8f620058,
+       0x00821023, 0x1840000c, 0x97a20020, 0xafa50028, 0xafa80034, 0xafa90038,
+       0xafa4003c, 0x34420020, 0x0a0005a8, 0xa7a20020, 0x8f680040, 0x00003021,
+       0x8f640058, 0x01002821, 0x3c026000, 0x8c434448, 0xaf63015c, 0x8f62004c,
+       0x01221023, 0x18400009, 0x00000000, 0x8f620054, 0x01221023, 0x1c400005,
+       0x97a20020, 0xafa50028, 0xafa90024, 0x0a0005c3, 0x34420040, 0x9742011a,
+       0x1440000c, 0x24020014, 0x8f620058, 0x14820009, 0x24020014, 0x8f63004c,
+       0x8f620054, 0x10620004, 0x97a20020, 0xafa50028, 0x34420080, 0xa7a20020,
+       0x24020014, 0x10e2000a, 0x28e20015, 0x10400005, 0x2402000c, 0x10e20006,
+       0x3c026000, 0x0a000600, 0x00000000, 0x24020016, 0x14e20031, 0x3c026000,
+       0x8f620054, 0x24420001, 0x1522002d, 0x3c026000, 0x24020014, 0x10e2001e,
+       0x28e20015, 0x10400005, 0x2402000c, 0x10e20008, 0x3c026000, 0x0a000600,
+       0x00000000, 0x24020016, 0x10e2000c, 0x97a20020, 0x0a000600, 0x3c026000,
+       0x97a30020, 0x2402000e, 0xafa50028, 0xa3a70022, 0xa3a20023, 0xafa90024,
+       0x34630054, 0x0a0005ff, 0xa7a30020, 0x24030010, 0x24040002, 0xafa50028,
+       0xa3a70022, 0xa3a30023, 0xa3a4001a, 0xafa90024, 0x0a0005fe, 0x3442005d,
+       0x97a20020, 0x24030012, 0x24040002, 0xafa50028, 0xa3a70022, 0xa3a30023,
+       0xa3a4001a, 0xafa90024, 0x3042fffe, 0x3442005c, 0xa7a20020, 0x3c026000,
+       0x8c434448, 0x31420001, 0xaf630160, 0x1040002c, 0x2402000c, 0x10e20014,
+       0x28e2000d, 0x10400005, 0x2402000a, 0x10e20008, 0x97a20020, 0x0a000631,
+       0x3c026000, 0x2402000e, 0x10e20018, 0x3c026000, 0x0a000631, 0x00000000,
+       0x24030008, 0x24040002, 0xafa50028, 0xa3a70022, 0xa3a30023, 0xa3a4001a,
+       0x0a00062f, 0x34420013, 0x97a30020, 0x30620004, 0x1440000b, 0x97a20020,
+       0x3462001b, 0xa7a20020, 0x24020016, 0x24030002, 0xafa50028, 0xa3a70022,
+       0xa3a20023, 0x0a000630, 0xa3a3001a, 0x97a20020, 0x24030010, 0x24040002,
+       0xafa50028, 0xa3a70022, 0xa3a30023, 0xa3a4001a, 0x3442001b, 0xa7a20020,
+       0x3c026000, 0x8c434448, 0x31420009, 0x0002102b, 0x00021023, 0x30420007,
+       0x34440003, 0xaf630164, 0x10c00016, 0x24030800, 0x8f820010, 0x27450180,
+       0x24420001, 0xaf820010, 0x24020004, 0xaf4301b8, 0xa4a40008, 0xa0a2000b,
+       0x93440120, 0x3c031000, 0xa4a6000e, 0xaca90024, 0xaca80028, 0x008b2021,
+       0xa4a4000c, 0xaf4301b8, 0x97a20020, 0x00003021, 0x3042ffbf, 0x0a000650,
+       0xa7a20020, 0x24060001, 0x3c026000, 0x8c434448, 0xaf630168, 0x97a20020,
+       0x10400020, 0x27450180, 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd,
+       0x00000000, 0x8f420128, 0xaca20000, 0x8fa30028, 0x240240c1, 0xa4a20008,
+       0xaca30018, 0x93a4001a, 0x24020002, 0xa0a2000b, 0xa0a4000a, 0x97a20020,
+       0xa4a20010, 0x93a30022, 0xa0a30012, 0x93a20023, 0xa0a20013, 0x8fa30024,
+       0xaca30014, 0x8fa20034, 0xaca20024, 0x8fa30038, 0xaca30028, 0x8fa2003c,
+       0x3c031000, 0xaca2002c, 0xaf4301b8, 0x3c026000, 0x8c434448, 0x00c01021,
+       0xaf63016c, 0x8fbf0048, 0x03e00008, 0x27bd0050, 0x8f460140, 0x8f470148,
+       0x3c028000, 0x00e24024, 0x00072c02, 0x30a300ff, 0x2402000b, 0x1062008f,
+       0x27440180, 0x2862000c, 0x10400011, 0x24020006, 0x1062005a, 0x28620007,
+       0x10400007, 0x24020008, 0x10600024, 0x24020001, 0x10620037, 0x00000000,
+       0x0a00077e, 0x00000000, 0x106200a9, 0x24020009, 0x106200bb, 0x00071c02,
+       0x0a00077e, 0x00000000, 0x2402001b, 0x106200c7, 0x2862001c, 0x10400007,
+       0x2402000e, 0x106200b1, 0x24020019, 0x106200c2, 0x00071c02, 0x0a00077e,
+       0x00000000, 0x24020080, 0x10620060, 0x28620081, 0x10400005, 0x2402001c,
+       0x10620094, 0x00071c02, 0x0a00077e, 0x00000000, 0x240200c2, 0x106200c5,
+       0x00a01821, 0x0a00077e, 0x00000000, 0x00a01821, 0x3c058000, 0x8f4201b8,
+       0x00451024, 0x1440fffd, 0x24020001, 0xa4830008, 0x24030002, 0xac860000,
+       0xac800004, 0xa082000a, 0xa083000b, 0xa4870010, 0x8f430144, 0x3c021000,
+       0xac800028, 0xac830024, 0x3c036000, 0xaf4201b8, 0x03e00008, 0xac600808,
+       0x11000009, 0x00a01821, 0x3c020800, 0x24030002, 0xa0434490, 0x24424490,
+       0xac460008, 0x8f430144, 0x03e00008, 0xac430004, 0x3c058000, 0x8f4201b8,
+       0x00451024, 0x1440fffd, 0x24020002, 0xac800000, 0xac860004, 0xa4830008,
+       0xa082000a, 0xa082000b, 0xa4870010, 0xac800024, 0x8f420144, 0x3c031000,
+       0xac820028, 0x3c026000, 0xaf4301b8, 0x03e00008, 0xac400808, 0x00a01821,
+       0x3c080800, 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, 0x00000000,
+       0xac860000, 0x91024490, 0x00002821, 0x10400002, 0x25064490, 0x8cc50008,
+       0xac850004, 0xa4830008, 0x91034490, 0x24020002, 0xa082000b, 0xa4870010,
+       0x34630001, 0xa083000a, 0x8f420144, 0xac820024, 0x91034490, 0x10600002,
+       0x00001021, 0x8cc20004, 0xac820028, 0x3c021000, 0xaf4201b8, 0x3c026000,
+       0xa1004490, 0x03e00008, 0xac400808, 0x00a01821, 0x3c058000, 0x8f4201b8,
+       0x00451024, 0x1440fffd, 0x24020002, 0xa082000b, 0xa4830008, 0xa4870010,
+       0x8f420144, 0x3c031000, 0xa4820012, 0x03e00008, 0xaf4301b8, 0x30e2ffff,
+       0x14400028, 0x00071c02, 0x93620005, 0x30420004, 0x14400020, 0x3c029000,
+       0x34420001, 0x00c21025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024,
+       0x1440fffd, 0x00000000, 0x93620005, 0x3c038000, 0x34630001, 0x00c31825,
+       0x34420004, 0xa3620005, 0xaf430020, 0x93620005, 0x30420004, 0x14400003,
+       0x3c038000, 0x0000000d, 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd,
+       0x24020005, 0x3c031000, 0xac860000, 0xa082000b, 0xaf4301b8, 0x0a00073d,
+       0x00071c02, 0x0000000d, 0x03e00008, 0x00000000, 0x00071c02, 0x3c058000,
+       0x8f4201b8, 0x00451024, 0x1440fffd, 0x24020001, 0xa4830008, 0x24030002,
+       0xac860000, 0xac800004, 0xa082000a, 0xa083000b, 0xa4870010, 0x8f430144,
+       0x3c021000, 0xac800028, 0xac830024, 0x03e00008, 0xaf4201b8, 0x00071c02,
+       0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, 0x24020002, 0xac800000,
+       0xac860004, 0xa4830008, 0xa082000a, 0xa082000b, 0xa4870010, 0xac800024,
+       0x8f420144, 0x3c031000, 0xac820028, 0x03e00008, 0xaf4301b8, 0x00071c02,
+       0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, 0x24020001, 0xa4830008,
+       0x24030002, 0xa082000a, 0x3c021000, 0xac860000, 0xac800004, 0xa083000b,
+       0xa4870010, 0xac800024, 0xac800028, 0x03e00008, 0xaf4201b8, 0x3c058000,
+       0x8f4201b8, 0x00451024, 0x1440fffd, 0x24020002, 0xac860000, 0xac800004,
+       0xa4830008, 0xa080000a, 0x0a000748, 0xa082000b, 0x0000000d, 0x03e00008,
+       0x00000000, 0x03e00008, 0x00000000, 0x8f420100, 0x3042003e, 0x14400011,
+       0x24020001, 0xaf400048, 0x8f420100, 0x304207c0, 0x10400005, 0x00000000,
+       0xaf40004c, 0xaf400050, 0x03e00008, 0x24020001, 0xaf400054, 0xaf400040,
+       0x8f420100, 0x30423800, 0x54400001, 0xaf400044, 0x24020001, 0x03e00008,
+       0x00000000, 0x3c029000, 0x34420001, 0x00822025, 0xaf440020, 0x3c038000,
+       0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x03e00008, 0x00000000,
+       0x3c028000, 0x34420001, 0x00822025, 0x03e00008, 0xaf440020, 0x8f430128,
+       0x27420180, 0xac430000, 0x8f650040, 0x240340c1, 0xa4430008, 0x24030002,
+       0xa044000a, 0x24040008, 0xa043000b, 0x3c031000, 0xa4440010, 0xa0400012,
+       0xa0400013, 0xac400014, 0xac400024, 0xac400028, 0xac40002c, 0xac450018,
+       0x03e00008, 0xaf4301b8, 0x24020001, 0xacc40000, 0x03e00008, 0xa4e50000,
+       0x03e00008, 0x24020001, 0x24020001, 0xaf400044, 0x03e00008, 0xaf400050,
+       0x00803021, 0x27450180, 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd,
+       0x00000000, 0x8f420128, 0xaca20000, 0x8cc30018, 0x240240c1, 0xa4a20008,
+       0xaca30018, 0x90c4000a, 0x24020002, 0xa0a2000b, 0xa0a4000a, 0x94c20010,
+       0xa4a20010, 0x90c30012, 0xa0a30012, 0x90c20013, 0xa0a20013, 0x8cc30014,
+       0xaca30014, 0x8cc20024, 0xaca20024, 0x8cc30028, 0xaca30028, 0x8cc2002c,
+       0x3c031000, 0xaca2002c, 0x24020001, 0xaf4301b8, 0xaf400044, 0x03e00008,
+       0xaf400050, 0x27bdffe8, 0xafbf0010, 0x0e000326, 0x00000000, 0x00002021,
+       0x0e00004c, 0xaf400180, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x8f460148,
+       0x27450180, 0x3c038000, 0x00061402, 0x304700ff, 0x8f4201b8, 0x00431024,
+       0x1440fffd, 0x00000000, 0x8f440140, 0x00061202, 0x304200ff, 0x00061c02,
+       0xaca20004, 0x24020002, 0xa4a30008, 0x30c300ff, 0xa0a2000b, 0xaca30024,
+       0x10e0000a, 0xaca40000, 0x28e20004, 0x14400005, 0x24020001, 0x24020005,
+       0x54e20005, 0xa0a0000a, 0x24020001, 0x0a000816, 0xa0a2000a, 0xa0a0000a,
+       0x3c021000, 0x03e00008, 0xaf4201b8, 0x03e00008, 0x00001021, 0x10c00007,
+       0x00000000, 0x8ca20000, 0x24c6ffff, 0x24a50004, 0xac820000, 0x14c0fffb,
+       0x24840004, 0x03e00008, 0x00000000, 0x0a00082a, 0x00a01021, 0xac860000,
+       0x24840004, 0x00a01021, 0x1440fffc, 0x24a5ffff, 0x03e00008, 0x00000000,
+       0x00000000 }; 
+
+static u32 bnx2_RXP_b06FwData[(0x0/4) + 1] = { 0x00000000 };
+static u32 bnx2_RXP_b06FwRodata[(0x0/4) + 1] = { 0x00000000 };
+static u32 bnx2_RXP_b06FwBss[(0x239c/4) + 1] = { 0x00000000 };
+static u32 bnx2_RXP_b06FwSbss[(0x14/4) + 1] = { 0x00000000 };
+
+static u32 bnx2_rv2p_proc1[] = {
+       0x00000008, 0xac000001, 0x0000000c, 0x2f800001, 0x00000010, 0x213f0004,
+       0x00000010, 0x20bf002c, 0x00000010, 0x203f0143, 0x00000018, 0x8000fffd,
+       0x00000010, 0xb1b8b017, 0x0000000b, 0x2fdf0002, 0x00000000, 0x03d80000,
+       0x00000000, 0x2c380000, 0x00000008, 0x2c800000, 0x00000008, 0x2d000000,
+       0x00000010, 0x91d40000, 0x00000008, 0x2d800108, 0x00000008, 0x02000002,
+       0x00000010, 0x91de0000, 0x0000000f, 0x42e0001c, 0x00000010, 0x91840a08,
+       0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008, 0x00000008, 0x2d800150,
+       0x00000000, 0x00000000, 0x00000010, 0x91de0000, 0x00000010, 0x2c620002,
+       0x00000018, 0x80000012, 0x0000000b, 0x2fdf0002, 0x0000000c, 0x1f800002,
+       0x00000000, 0x2c070000, 0x00000018, 0x8000ffe6, 0x00000008, 0x02000002,
+       0x0000000f, 0x42e0001c, 0x00000010, 0x91840a08, 0x00000008, 0x2c8000b0,
+       0x00000008, 0x2d000008, 0x00000010, 0x91d40000, 0x00000008, 0x2d800108,
+       0x00000000, 0x00000000, 0x00000010, 0x91de0000, 0x00000018, 0x80000004,
+       0x0000000c, 0x1f800002, 0x00000000, 0x00000000, 0x00000018, 0x8000ffd9,
+       0x0000000c, 0x29800002, 0x0000000c, 0x1f800002, 0x00000000, 0x2adf0000,
+       0x00000008, 0x2a000005, 0x00000018, 0x8000ffd4, 0x00000008, 0x02240030,
+       0x00000018, 0x00040000, 0x00000018, 0x80000015, 0x00000018, 0x80000017,
+       0x00000018, 0x8000001b, 0x00000018, 0x8000004c, 0x00000018, 0x8000008c,
+       0x00000018, 0x8000000f, 0x00000018, 0x8000000e, 0x00000018, 0x8000000d,
+       0x00000018, 0x8000000c, 0x00000018, 0x800000c2, 0x00000018, 0x8000000a,
+       0x00000018, 0x80000009, 0x00000018, 0x80000008, 0x00000018, 0x800000fd,
+       0x00000018, 0x80000006, 0x00000018, 0x80000005, 0x00000018, 0x800000ff,
+       0x00000018, 0x80000104, 0x00000018, 0x80000002, 0x00000018, 0x80000098,
+       0x00000018, 0x80000000, 0x0000000c, 0x1f800001, 0x00000000, 0x00000000,
+       0x00000018, 0x8000ffba, 0x00000010, 0x91d40000, 0x0000000c, 0x29800001,
+       0x0000000c, 0x1f800001, 0x00000008, 0x2a000002, 0x00000018, 0x8000ffb5,
+       0x00000010, 0xb1a0b012, 0x0000000b, 0x2fdf0002, 0x00000000, 0x2c200000,
+       0x00000008, 0x2c800000, 0x00000008, 0x2d000000, 0x00000010, 0x91d40000,
+       0x00000008, 0x2d80011c, 0x00000000, 0x00000000, 0x00000010, 0x91de0000,
+       0x0000000f, 0x47600008, 0x0000000f, 0x060e0001, 0x00000010, 0x001f0000,
+       0x00000000, 0x0f580000, 0x00000000, 0x0a640000, 0x00000000, 0x0ae50000,
+       0x00000000, 0x0b660000, 0x00000000, 0x0d610000, 0x00000018, 0x80000013,
+       0x0000000f, 0x47600008, 0x0000000b, 0x2fdf0002, 0x00000008, 0x2c800000,
+       0x00000008, 0x2d000000, 0x00000010, 0x91d40000, 0x00000008, 0x2d80011c,
+       0x0000000f, 0x060e0001, 0x00000010, 0x001f0000, 0x00000000, 0x0f580000,
+       0x00000010, 0x91de0000, 0x00000000, 0x0a640000, 0x00000000, 0x0ae50000,
+       0x00000000, 0x0b660000, 0x00000000, 0x0d610000, 0x00000000, 0x02620000,
+       0x0000000b, 0x2fdf0002, 0x00000000, 0x309a0000, 0x00000000, 0x31040000,
+       0x00000000, 0x0c961800, 0x00000009, 0x0c99ffff, 0x00000004, 0xcc993400,
+       0x00000010, 0xb1963202, 0x00000008, 0x0f800000, 0x0000000c, 0x29800001,
+       0x00000010, 0x00220002, 0x0000000c, 0x29520001, 0x0000000c, 0x29520000,
+       0x00000008, 0x22000001, 0x0000000c, 0x1f800001, 0x00000000, 0x2adf0000,
+       0x00000008, 0x2a000003, 0x00000018, 0x8000ff83, 0x00000010, 0xb1a0b01d,
+       0x0000000b, 0x2fdf0002, 0x00000000, 0x2c200000, 0x00000008, 0x2c8000b0,
+       0x00000008, 0x2d000008, 0x00000010, 0x91d40000, 0x00000008, 0x2d800150,
+       0x00000000, 0x00000000, 0x00000010, 0x205f0000, 0x00000008, 0x2c800000,
+       0x00000008, 0x2d000000, 0x00000008, 0x2d800108, 0x00000000, 0x00000000,
+       0x00000010, 0x91de0000, 0x0000000f, 0x47600008, 0x00000000, 0x060e0000,
+       0x00000010, 0x001f0000, 0x00000000, 0x0f580000, 0x00000010, 0x91de0000,
+       0x00000000, 0x0a640000, 0x00000000, 0x0ae50000, 0x00000000, 0x0b670000,
+       0x00000000, 0x0d620000, 0x00000000, 0x0ce71800, 0x00000009, 0x0c99ffff,
+       0x00000004, 0xcc993400, 0x00000010, 0xb1963220, 0x00000008, 0x0f800000,
+       0x00000018, 0x8000001e, 0x0000000f, 0x47600008, 0x0000000b, 0x2fdf0002,
+       0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008, 0x00000010, 0x91d40000,
+       0x00000008, 0x2d80012c, 0x0000000f, 0x060e0001, 0x00000010, 0x001f0000,
+       0x00000000, 0x0f580000, 0x00000010, 0x91de0000, 0x00000000, 0x0a640000,
+       0x00000000, 0x0ae50000, 0x00000000, 0x0b670000, 0x00000000, 0x0d620000,
+       0x00000000, 0x02630000, 0x0000000f, 0x47620010, 0x00000000, 0x0ce71800,
+       0x0000000b, 0x2fdf0002, 0x00000000, 0x311a0000, 0x00000000, 0x31840000,
+       0x0000000b, 0xc20000ff, 0x00000002, 0x42040000, 0x00000001, 0x31620800,
+       0x0000000f, 0x020e0010, 0x00000002, 0x31620800, 0x00000009, 0x0c99ffff,
+       0x00000004, 0xcc993400, 0x00000010, 0xb1963202, 0x00000008, 0x0f800000,
+       0x0000000c, 0x29800001, 0x0000000c, 0x1f800001, 0x0000000c, 0x61420006,
+       0x00000008, 0x22000008, 0x00000000, 0x2adf0000, 0x00000008, 0x2a000004,
+       0x00000018, 0x8000ff42, 0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008,
+       0x00000010, 0x91a0b008, 0x00000010, 0x91d40000, 0x0000000c, 0x31620018,
+       0x00000008, 0x2d800001, 0x00000000, 0x00000000, 0x00000010, 0x91de0000,
+       0x00000008, 0xac000001, 0x00000018, 0x8000000e, 0x00000000, 0x0380b000,
+       0x0000000b, 0x2fdf0002, 0x00000000, 0x2c004000, 0x00000010, 0x91d40000,
+       0x00000008, 0x2d800101, 0x00000000, 0x00000000, 0x00000010, 0x91de0000,
+       0x0000000c, 0x31620018, 0x00000008, 0x2d800001, 0x00000000, 0x00000000,
+       0x00000010, 0x91de0000, 0x0000000b, 0x2fdf0002, 0x00000000, 0x2c000e00,
+       0x0000000c, 0x29800001, 0x0000000c, 0x1f800001, 0x00000008, 0x2a000007,
+       0x00000018, 0x8000ff27, 0x00000010, 0xb1a0b016, 0x0000000b, 0x2fdf0002,
+       0x00000000, 0x03d80000, 0x00000000, 0x2c200000, 0x00000008, 0x2c8000b0,
+       0x00000008, 0x2d000008, 0x00000010, 0x91d40000, 0x00000008, 0x2d800150,
+       0x00000000, 0x00000000, 0x00000010, 0x205f0000, 0x00000008, 0x2c800000,
+       0x00000008, 0x2d000000, 0x00000008, 0x2d800108, 0x00000008, 0x07000001,
+       0x00000010, 0xb5de1c00, 0x00000010, 0x2c620002, 0x00000018, 0x8000000a,
+       0x0000000b, 0x2fdf0002, 0x00000000, 0x2c070000, 0x0000000c, 0x1f800001,
+       0x00000010, 0x91de0000, 0x00000018, 0x8000ff11, 0x00000008, 0x2c8000b0,
+       0x00000008, 0x2d000008, 0x00000010, 0x91d40000, 0x00000008, 0x2d800108,
+       0x0000000c, 0x29800001, 0x0000000c, 0x1f800001, 0x00000010, 0x91de0000,
+       0x00000000, 0x2adf0000, 0x00000008, 0x2a00000a, 0x00000018, 0x8000ff07,
+       0x00000000, 0x82265600, 0x0000000f, 0x47220008, 0x00000009, 0x070e000f,
+       0x00000008, 0x070e0008, 0x00000008, 0x02800001, 0x00000007, 0x02851c00,
+       0x00000008, 0x82850001, 0x00000000, 0x02840a00, 0x00000007, 0x42851c00,
+       0x00000003, 0xc3aa5200, 0x00000000, 0x03b10e00, 0x00000010, 0x001f0000,
+       0x0000000f, 0x0f280007, 0x00000007, 0x4b071c00, 0x00000000, 0x00000000,
+       0x0000000f, 0x0a960003, 0x00000000, 0x0a955c00, 0x00000000, 0x4a005a00,
+       0x00000000, 0x0c960a00, 0x00000009, 0x0c99ffff, 0x00000008, 0x0d00ffff,
+       0x00000010, 0xb1963202, 0x00000008, 0x0f800005, 0x00000010, 0x00220020,
+       0x00000000, 0x02a70000, 0x00000010, 0xb1850002, 0x00000008, 0x82850200,
+       0x00000000, 0x02000000, 0x00000000, 0x03a60000, 0x00000018, 0x8000004e,
+       0x00000000, 0x072b0000, 0x00000001, 0x878c1c00, 0x00000000, 0x870e1e00,
+       0x00000000, 0x860c1e00, 0x00000000, 0x03061e00, 0x00000010, 0xb18e0003,
+       0x00000018, 0x80000047, 0x00000018, 0x8000fffa, 0x00000010, 0x918c0003,
+       0x00000010, 0xb1870002, 0x00000018, 0x80000043, 0x00000010, 0x91d40000,
+       0x0000000c, 0x29800001, 0x00000000, 0x2a860000, 0x00000000, 0x230c0000,
+       0x00000000, 0x2b070000, 0x00000010, 0xb187000e, 0x00000008, 0x2a000008,
+       0x00000018, 0x8000003b, 0x00000010, 0x91d40000, 0x00000000, 0x28d18c00,
+       0x00000000, 0x2a860000, 0x00000000, 0x230c0000, 0x00000000, 0x2b070000,
+       0x00000018, 0x8000fff8, 0x00000010, 0x91d40000, 0x0000000c, 0x29800001,
+       0x00000000, 0x2aab0000, 0x00000000, 0xa3265600, 0x00000000, 0x2b000000,
+       0x0000000c, 0x1f800001, 0x00000008, 0x2a000008, 0x00000018, 0x8000fec8,
+       0x00000010, 0x91d40000, 0x0000000c, 0x29800001, 0x0000000c, 0x1f800001,
+       0x00000008, 0x2a000009, 0x00000018, 0x8000fec3, 0x00000010, 0x91d40000,
+       0x0000000c, 0x29800001, 0x0000000c, 0x1f800001, 0x00000000, 0x29420000,
+       0x00000008, 0x2a000002, 0x00000018, 0x8000febd, 0x00000018, 0x8000febc,
+       0x00000010, 0xb1bcb016, 0x0000000b, 0x2fdf0002, 0x00000000, 0x03d80000,
+       0x00000000, 0x2c3c0000, 0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008,
+       0x00000010, 0x91d40000, 0x00000008, 0x2d800150, 0x00000000, 0x00000000,
+       0x00000010, 0x205f0000, 0x00000008, 0x2c800000, 0x00000008, 0x2d000000,
+       0x00000008, 0x2d800108, 0x00000008, 0x07000001, 0x00000010, 0xb5de1c00,
+       0x00000010, 0x2c620002, 0x00000018, 0x8000000a, 0x0000000b, 0x2fdf0002,
+       0x00000000, 0x2c070000, 0x0000000c, 0x1f800000, 0x00000010, 0x91de0000,
+       0x00000018, 0x8000fea6, 0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008,
+       0x00000010, 0x91d40000, 0x00000008, 0x2d800108, 0x0000000c, 0x29800000,
+       0x0000000c, 0x1f800000, 0x00000010, 0x91de0000, 0x00000000, 0x2adf0000,
+       0x00000008, 0x2a000006, 0x00000018, 0x8000fe9c, 0x00000008, 0x03050004,
+       0x00000006, 0x83040c00, 0x00000008, 0x02850200, 0x00000000, 0x86050c00,
+       0x00000001, 0x860c0e00, 0x00000008, 0x02040004, 0x00000000, 0x02041800,
+       0x00000000, 0x83871800, 0x00000018, 0x00020000 };
+
+static u32 bnx2_rv2p_proc2[] = {
+       0x00000000, 0x2a000000, 0x00000010, 0xb1d40000, 0x00000008, 0x02540003,
+       0x00000018, 0x00040000, 0x00000018, 0x8000000a, 0x00000018, 0x8000000a,
+       0x00000018, 0x8000000e, 0x00000018, 0x80000056, 0x00000018, 0x800001b9,
+       0x00000018, 0x800001e1, 0x00000018, 0x8000019b, 0x00000018, 0x800001f9,
+       0x00000018, 0x8000019f, 0x00000018, 0x800001a6, 0x00000018, 0x80000000,
+       0x0000000c, 0x29800001, 0x00000000, 0x2a000000, 0x0000000c, 0x29800000,
+       0x00000010, 0x20530000, 0x00000018, 0x8000ffee, 0x0000000c, 0x29800001,
+       0x00000010, 0x91de0000, 0x00000010, 0x001f0000, 0x00000000, 0x2f80aa00,
+       0x00000000, 0x2a000000, 0x00000000, 0x0d610000, 0x00000000, 0x03620000,
+       0x00000000, 0x2c400000, 0x00000000, 0x02638c00, 0x00000000, 0x26460000,
+       0x00000010, 0x00420002, 0x00000008, 0x02040012, 0x00000010, 0xb9060836,
+       0x00000000, 0x0f580000, 0x00000000, 0x0a640000, 0x00000000, 0x0ae50000,
+       0x00000000, 0x0b660000, 0x00000000, 0x0c000000, 0x00000000, 0x0b800000,
+       0x00000010, 0x00420009, 0x00000008, 0x0cc60012, 0x00000008, 0x0f800003,
+       0x00000000, 0x00000000, 0x00000010, 0x009f0000, 0x00000008, 0x27110012,
+       0x00000000, 0x66900000, 0x00000008, 0xa31b0012, 0x00000018, 0x80000008,
+       0x00000000, 0x0cc60000, 0x00000008, 0x0f800003, 0x00000000, 0x00000000,
+       0x00000010, 0x009f0000, 0x00000000, 0x27110000, 0x00000000, 0x66900000,
+       0x00000000, 0x231b0000, 0x00000010, 0xb197320e, 0x00000000, 0x25960000,
+       0x00000000, 0x021b0000, 0x00000010, 0x001f0000, 0x00000008, 0x0f800003,
+       0x0000000c, 0x29800000, 0x00000010, 0x20530000, 0x00000000, 0x22c50800,
+       0x00000010, 0x009f0000, 0x00000000, 0x27002200, 0x00000000, 0x26802000,
+       0x00000000, 0x231b0000, 0x0000000c, 0x69520001, 0x00000018, 0x8000fff3,
+       0x00000010, 0x01130002, 0x00000010, 0xb1980003, 0x00000010, 0x001f0000,
+       0x00000008, 0x0f800004, 0x00000008, 0x22000003, 0x00000008, 0x2c80000c,
+       0x00000008, 0x2d00000c, 0x00000010, 0x009f0000, 0x00000000, 0x25960000,
+       0x0000000c, 0x29800000, 0x00000000, 0x32140000, 0x00000000, 0x32950000,
+       0x00000000, 0x33160000, 0x00000000, 0x31e32e00, 0x00000008, 0x2d800010,
+       0x00000010, 0x20530000, 0x00000018, 0x8000ffac, 0x00000000, 0x23000000,
+       0x00000000, 0x25e60000, 0x00000008, 0x2200000b, 0x0000000c, 0x69520000,
+       0x0000000c, 0x29800000, 0x00000010, 0x20530000, 0x00000018, 0x8000ffa5,
+       0x0000000c, 0x29800001, 0x00000010, 0x91de0000, 0x00000000, 0x2fd50000,
+       0x00000010, 0x001f0000, 0x00000000, 0x02700000, 0x00000000, 0x0d620000,
+       0x00000000, 0xbb630800, 0x00000000, 0x2a000000, 0x00000009, 0x076000ff,
+       0x0000000f, 0x2c0e0007, 0x00000008, 0x2c800000, 0x00000008, 0x2d000064,
+       0x00000008, 0x2d80011c, 0x00000009, 0x06420002, 0x0000000c, 0x61420001,
+       0x00000000, 0x0f400000, 0x00000000, 0x02d08c00, 0x00000000, 0x23000000,
+       0x00000004, 0x826da000, 0x00000000, 0x8304a000, 0x00000000, 0x22c50c00,
+       0x00000000, 0x03760000, 0x00000004, 0x83860a00, 0x00000000, 0x83870c00,
+       0x00000010, 0x91de0000, 0x00000000, 0x037c0000, 0x00000000, 0x837b0c00,
+       0x00000001, 0x83060e00, 0x00000000, 0x83870c00, 0x00000000, 0x82850e00,
+       0x00000010, 0xb1860016, 0x0000000f, 0x47610018, 0x00000000, 0x068e0000,
+       0x0000000f, 0x47670010, 0x0000000f, 0x47e20010, 0x00000000, 0x870e1e00,
+       0x00000010, 0xb70e1a10, 0x00000010, 0x0ce7000e, 0x00000008, 0x22000009,
+       0x00000000, 0x286d0000, 0x0000000f, 0x65680010, 0x00000003, 0xf66c9400,
+       0x00000010, 0xb972a003, 0x0000000c, 0x73e70019, 0x0000000c, 0x21420004,
+       0x00000018, 0x8000023f, 0x00000000, 0x37ed0000, 0x0000000c, 0x73e7001a,
+       0x00000010, 0x20530000, 0x00000008, 0x22000008, 0x0000000c, 0x61420004,
+       0x00000000, 0x02f60000, 0x00000004, 0x82840a00, 0x00000010, 0xb1840a2b,
+       0x00000010, 0x2d67000a, 0x00000010, 0xb96d0804, 0x00000004, 0xb6ed0a00,
+       0x00000000, 0x37ed0000, 0x00000018, 0x80000029, 0x0000000c, 0x61420000,
+       0x00000000, 0x37040000, 0x00000000, 0x37850000, 0x0000000c, 0x33e7001a,
+       0x00000018, 0x80000024, 0x00000010, 0xb96d0809, 0x00000004, 0xb6ed0a00,
+       0x00000000, 0x036d0000, 0x00000004, 0xb76e0c00, 0x00000010, 0x91ee0c1f,
+       0x0000000c, 0x73e7001a, 0x00000004, 0xb6ef0c00, 0x00000000, 0x37ed0000,
+       0x00000018, 0x8000001b, 0x0000000c, 0x61420000, 0x00000010, 0xb7ee0a05,
+       0x00000010, 0xb96f0815, 0x00000003, 0xb76e0800, 0x00000004, 0xb7ef0a00,
+       0x00000018, 0x80000015, 0x00000010, 0x0ce7000c, 0x00000008, 0x22000009,
+       0x00000000, 0x286d0000, 0x0000000f, 0x65680010, 0x00000003, 0xf66c9400,
+       0x00000010, 0xb972a003, 0x0000000c, 0x73e70019, 0x0000000c, 0x21420004,
+       0x00000018, 0x80000215, 0x00000010, 0x20530000, 0x00000008, 0x22000008,
+       0x0000000c, 0x61420004, 0x00000000, 0x37040000, 0x00000000, 0x37850000,
+       0x00000000, 0x036d0000, 0x00000003, 0xb8f10c00, 0x00000018, 0x80000004,
+       0x00000000, 0x02840000, 0x00000002, 0x21421800, 0x0000000c, 0x61420000,
+       0x00000000, 0x286d0000, 0x0000000f, 0x65ed0010, 0x00000009, 0x266dffff,
+       0x00000000, 0x23000000, 0x00000010, 0xb1840a3d, 0x00000010, 0x01420002,
+       0x00000004, 0xb8f10a00, 0x00000003, 0x83760a00, 0x00000010, 0xb8040c39,
+       0x00000010, 0xb7e6080a, 0x00000000, 0x0a640000, 0x00000000, 0x0ae50000,
+       0x00000009, 0x0c68ffff, 0x00000009, 0x0b67ffff, 0x00000000, 0x0be60000,
+       0x00000000, 0x0c840000, 0x00000010, 0xb197320c, 0x00000008, 0x0f800002,
+       0x00000018, 0x8000000a, 0x00000000, 0x0a6a0000, 0x00000000, 0x0aeb0000,
+       0x00000000, 0x0c000000, 0x00000009, 0x0b6cffff, 0x00000000, 0x0be90000,
+       0x00000000, 0x0c840000, 0x00000010, 0xb1973203, 0x00000008, 0x0f800002,
+       0x00000018, 0x80000001, 0x00000010, 0x001f0000, 0x00000000, 0x0c860000,
+       0x00000000, 0x06980000, 0x00000008, 0x0f800003, 0x00000000, 0x00000000,
+       0x00000010, 0x009f0000, 0x00000010, 0xb1973210, 0x00000000, 0x231b0000,
+       0x00000000, 0x02043600, 0x00000003, 0x8384a000, 0x0000000f, 0x65870010,
+       0x00000009, 0x2607ffff, 0x00000000, 0x27111a00, 0x00000000, 0x66900000,
+       0x0000000c, 0x29000000, 0x00000018, 0x800001de, 0x00000000, 0x06980000,
+       0x00000010, 0x20530000, 0x00000000, 0x22c58c00, 0x00000010, 0x001f0000,
+       0x00000008, 0x0f800003, 0x00000018, 0x8000fff0, 0x00000000, 0x02043600,
+       0x00000000, 0x231b0000, 0x00000003, 0x8384a000, 0x0000000f, 0x65870010,
+       0x00000009, 0x2607ffff, 0x00000000, 0x27111a00, 0x00000000, 0x66900000,
+       0x0000000c, 0x29000000, 0x00000010, 0x91840a02, 0x00000002, 0x21421800,
+       0x00000000, 0x32140000, 0x00000000, 0x32950000, 0x00000005, 0x73e72c00,
+       0x00000005, 0x74683000, 0x00000000, 0x33170000, 0x00000018, 0x80000138,
+       0x00000010, 0x91c60004, 0x00000008, 0x07000004, 0x00000010, 0xb1c41c02,
+       0x00000010, 0x91840a04, 0x00000018, 0x800001c3, 0x00000010, 0x20530000,
+       0x00000000, 0x22c58c00, 0x00000010, 0xb1840a8e, 0x0000000c, 0x21420006,
+       0x00000010, 0x0ce7001a, 0x0000000f, 0x43680010, 0x00000000, 0x03f30c00,
+       0x00000010, 0x91870850, 0x0000000f, 0x46ec0010, 0x00000010, 0xb68d0c4e,
+       0x00000000, 0x838d0c00, 0x00000000, 0xa3050800, 0x00000001, 0xa3460e00,
+       0x00000000, 0x02048c00, 0x00000010, 0x91840a02, 0x00000002, 0x21421800,
+       0x00000010, 0x001f0000, 0x00000008, 0x22000008, 0x00000003, 0x8384a000,
+       0x0000000f, 0x65870010, 0x00000009, 0x2607ffff, 0x00000000, 0x27750c00,
+       0x00000000, 0x66f40000, 0x0000000c, 0x29000000, 0x00000018, 0x800001aa,
+       0x00000000, 0x03068c00, 0x00000003, 0xf4680c00, 0x00000010, 0x20530000,
+       0x00000000, 0x22c58c00, 0x00000018, 0x8000ffe5, 0x00000000, 0x39760000,
+       0x00000000, 0x39840000, 0x0000000c, 0x33e70019, 0x00000010, 0x001f0000,
+       0x00000000, 0x031e0000, 0x00000000, 0x0760fe00, 0x0000000f, 0x0f0e0007,
+       0x00000000, 0x83850800, 0x00000000, 0x0a7d0000, 0x00000000, 0x0afe0000,
+       0x00000000, 0x0b7f0000, 0x00000000, 0x0d7a0000, 0x00000000, 0x0c000000,
+       0x00000000, 0x0bfc0000, 0x00000000, 0x0c970e00, 0x00000008, 0x0f800003,
+       0x0000000f, 0x47670010, 0x00000008, 0x070e0001, 0x0000000b, 0xc38000ff,
+       0x00000002, 0x43870000, 0x00000001, 0x33e70e00, 0x0000000f, 0x038e0010,
+       0x00000002, 0x33e70e00, 0x00000000, 0x28f30000, 0x00000010, 0x009f0000,
+       0x00000000, 0x02043600, 0x00000010, 0x91840a02, 0x00000002, 0x21421800,
+       0x00000008, 0x22000006, 0x00000000, 0x231b0000, 0x00000000, 0x23ff0000,
+       0x00000000, 0x241b0000, 0x00000003, 0x8384a000, 0x0000000f, 0x65870010,
+       0x00000009, 0x2607ffff, 0x00000000, 0x27110000, 0x00000000, 0x26900000,
+       0x0000000c, 0x29000000, 0x00000018, 0x8000017e, 0x00000003, 0xf4683600,
+       0x00000000, 0x3a100000, 0x00000000, 0x3a910000, 0x00000003, 0xf66c2400,
+       0x00000010, 0x001f0000, 0x00000010, 0xb1923604, 0x00000008, 0x0f800004,
+       0x00000000, 0x00000000, 0x00000010, 0x009f0000, 0x00000000, 0x3e170000,
+       0x00000000, 0x3e940000, 0x00000000, 0x3f150000, 0x00000000, 0x3f960000,
+       0x00000010, 0x001f0000, 0x00000000, 0x0f060000, 0x00000010, 0x20530000,
+       0x00000000, 0x22c53600, 0x00000018, 0x8000ffac, 0x00000010, 0x001f0000,
+       0x00000000, 0x031e0000, 0x00000000, 0x83850800, 0x00000009, 0x076000ff,
+       0x0000000f, 0x0f0e0007, 0x00000000, 0x0c000000, 0x00000000, 0x0a7d0000,
+       0x00000000, 0x0afe0000, 0x00000000, 0x0b7f0000, 0x00000000, 0x0d7a0000,
+       0x00000000, 0x0bfc0000, 0x00000000, 0x0c970e00, 0x00000008, 0x0f800003,
+       0x0000000f, 0x47670010, 0x00000008, 0x070e0001, 0x0000000b, 0xc38000ff,
+       0x00000002, 0x43870000, 0x00000001, 0x33e70e00, 0x0000000f, 0x038e0010,
+       0x00000002, 0x33e70e00, 0x00000000, 0x39840000, 0x00000003, 0xb9720800,
+       0x00000000, 0x28f30000, 0x0000000f, 0x65680010, 0x00000010, 0x009f0000,
+       0x00000000, 0x02043600, 0x00000010, 0x91840a02, 0x00000002, 0x21421800,
+       0x00000008, 0x22000007, 0x00000000, 0x231b0000, 0x00000000, 0x23ff0000,
+       0x00000000, 0x241b0000, 0x00000003, 0x8384a000, 0x0000000f, 0x65870010,
+       0x00000009, 0x2607ffff, 0x00000000, 0x27110000, 0x00000000, 0x26900000,
+       0x0000000c, 0x29000000, 0x00000018, 0x80000145, 0x00000003, 0xf4683600,
+       0x00000000, 0x3a100000, 0x00000000, 0x3a910000, 0x00000003, 0xf66c2400,
+       0x00000010, 0x001f0000, 0x00000010, 0xb1923604, 0x00000008, 0x0f800004,
+       0x00000000, 0x00000000, 0x00000010, 0x009f0000, 0x00000000, 0x3e170000,
+       0x00000000, 0x3e940000, 0x00000000, 0x3f150000, 0x00000000, 0x3f960000,
+       0x00000010, 0x001f0000, 0x00000000, 0x0f060000, 0x00000010, 0x20530000,
+       0x00000000, 0x22c53600, 0x00000018, 0x8000ff73, 0x00000010, 0x0ce70005,
+       0x00000008, 0x2c80000c, 0x00000008, 0x2d000070, 0x00000008, 0x2d800010,
+       0x00000000, 0x00000000, 0x00000010, 0x205f0000, 0x00000018, 0x8000011d,
+       0x00000000, 0x2c1e0000, 0x00000008, 0x2c8000b8, 0x00000008, 0x2d000010,
+       0x00000008, 0x2d800048, 0x00000000, 0x00000000, 0x00000010, 0x91de0000,
+       0x00000018, 0x8000fe5d, 0x0000000c, 0x29800001, 0x00000000, 0x2a000000,
+       0x00000010, 0x001f0000, 0x00000000, 0x0f008000, 0x00000008, 0x0f800007,
+       0x00000018, 0x80000006, 0x0000000c, 0x29800001, 0x00000000, 0x2a000000,
+       0x00000010, 0x001f0000, 0x0000000f, 0x0f470007, 0x00000008, 0x0f800008,
+       0x00000018, 0x80000119, 0x00000010, 0x20530000, 0x00000018, 0x8000fe4f,
+       0x0000000c, 0x29800001, 0x00000010, 0x91de0000, 0x00000000, 0x2fd50000,
+       0x00000000, 0x2a000000, 0x00000009, 0x0261ffff, 0x0000000d, 0x70e10001,
+       0x00000018, 0x80000101, 0x00000000, 0x2c400000, 0x00000008, 0x2c8000c4,
+       0x00000008, 0x2d00001c, 0x00000008, 0x2d800001, 0x00000005, 0x70e10800,
+       0x00000010, 0x91de0000, 0x00000018, 0x8000fe41, 0x0000000c, 0x29800001,
+       0x00000010, 0x91de0000, 0x00000000, 0x2fd50000, 0x00000010, 0x001f0000,
+       0x00000000, 0x02700000, 0x00000000, 0x0d620000, 0x00000000, 0xbb630800,
+       0x00000000, 0x2a000000, 0x00000000, 0x0f400000, 0x00000000, 0x2c400000,
+       0x0000000c, 0x73e7001b, 0x00000010, 0x0ce7000e, 0x00000000, 0x286d0000,
+       0x0000000f, 0x65ed0010, 0x00000009, 0x266dffff, 0x00000018, 0x80000069,
+       0x00000008, 0x02000004, 0x00000010, 0x91c40803, 0x00000018, 0x800000f6,
+       0x00000010, 0x20530000, 0x00000018, 0x800000e5, 0x00000008, 0x2c8000b8,
+       0x00000008, 0x2d000010, 0x00000008, 0x2d800048, 0x00000018, 0x80000005,
+       0x00000008, 0x2c8000c4, 0x00000008, 0x2d00001c, 0x00000008, 0x2d800001,
+       0x00000000, 0x00000000, 0x00000010, 0x205f0000, 0x00000008, 0x2c800048,
+       0x00000008, 0x2d000068, 0x00000008, 0x2d800104, 0x00000000, 0x00000000,
+       0x00000010, 0x91de0000, 0x00000000, 0x27f60000, 0x00000010, 0xb87a9e04,
+       0x00000008, 0x2200000d, 0x00000018, 0x800000e2, 0x00000010, 0x20530000,
+       0x00000018, 0x8000fe18, 0x0000000c, 0x29800001, 0x00000010, 0x91de0000,
+       0x00000000, 0x2fd50000, 0x00000010, 0x001f0000, 0x00000000, 0x02700000,
+       0x00000000, 0x0d620000, 0x00000000, 0xbb630800, 0x00000000, 0x2a000000,
+       0x00000010, 0x0e670011, 0x00000000, 0x286d0000, 0x0000000f, 0x65ed0010,
+       0x00000009, 0x266dffff, 0x00000004, 0xb8f1a000, 0x00000000, 0x0f400000,
+       0x0000000c, 0x73e7001c, 0x00000018, 0x80000040, 0x00000008, 0x02000004,
+       0x00000010, 0x91c40802, 0x00000018, 0x800000cd, 0x00000000, 0x2c1e0000,
+       0x00000008, 0x2c8000b8, 0x00000008, 0x2d000010, 0x00000008, 0x2d800048,
+       0x00000010, 0x20530000, 0x00000010, 0x91de0000, 0x00000018, 0x8000fdfe,
+       0x0000000c, 0x29800001, 0x00000000, 0x03550000, 0x00000000, 0x06460000,
+       0x00000000, 0x03d60000, 0x00000000, 0x2a000000, 0x0000000f, 0x0f480007,
+       0x00000010, 0xb18c0027, 0x0000000f, 0x47420008, 0x00000009, 0x070e000f,
+       0x00000008, 0x070e0008, 0x00000010, 0x001f0000, 0x00000008, 0x09000001,
+       0x00000007, 0x09121c00, 0x00000003, 0xcbca9200, 0x00000000, 0x0b97a200,
+       0x00000007, 0x4b171c00, 0x0000000f, 0x0a960003, 0x00000000, 0x0a959c00,
+       0x00000000, 0x4a009a00, 0x00000008, 0x82120001, 0x00000001, 0x0c170800,
+       0x00000000, 0x02180000, 0x00000000, 0x0c971800, 0x00000008, 0x0d00ffff,
+       0x00000008, 0x0f800006, 0x0000000c, 0x29000000, 0x00000008, 0x22000001,
+       0x00000000, 0x22c50c00, 0x00000010, 0x009f0000, 0x00000010, 0xb197320b,
+       0x00000000, 0x231b0000, 0x00000000, 0x27110800, 0x00000000, 0x66900000,
+       0x00000018, 0x800000a4, 0x00000000, 0x02180000, 0x00000010, 0x20530000,
+       0x00000000, 0x22c53600, 0x00000010, 0x001f0000, 0x00000008, 0x0f800006,
+       0x00000018, 0x8000fff5, 0x00000010, 0x91870002, 0x00000008, 0x2200000a,
+       0x00000000, 0x231b0000, 0x00000000, 0x27110800, 0x00000000, 0x66900000,
+       0x00000018, 0x80000098, 0x00000008, 0x0200000a, 0x00000010, 0x91c40804,
+       0x00000010, 0x02c20003, 0x00000010, 0x001f0000, 0x00000008, 0x0f800008,
+       0x00000010, 0x20530000, 0x00000018, 0x8000fdc9, 0x00000000, 0x06820000,
+       0x00000010, 0x001f0000, 0x00000010, 0x0ce70028, 0x00000000, 0x03720000,
+       0x00000000, 0xa8760c00, 0x00000000, 0x0cf60000, 0x00000010, 0xb8723224,
+       0x00000000, 0x03440000, 0x00000008, 0x22000010, 0x00000000, 0x03ca0000,
+       0x0000000f, 0x65680010, 0x00000000, 0x0bcf0000, 0x00000000, 0x27f20000,
+       0x00000010, 0xb7ef3203, 0x0000000c, 0x21420004, 0x0000000c, 0x73e70019,
+       0x00000000, 0x07520000, 0x00000000, 0x29000000, 0x00000018, 0x8000007e,
+       0x00000004, 0xb9723200, 0x00000010, 0x20530000, 0x00000000, 0x22060000,
+       0x0000000c, 0x61420004, 0x00000000, 0x25070000, 0x00000000, 0x27970000,
+       0x00000000, 0x290e0000, 0x00000010, 0x0ce70010, 0x00000010, 0xb873320f,
+       0x0000000f, 0x436c0010, 0x00000000, 0x03f30c00, 0x00000000, 0x03f30000,
+       0x00000000, 0x83990e00, 0x00000001, 0x83860e00, 0x00000000, 0x83060e00,
+       0x00000003, 0xf66c0c00, 0x00000000, 0x39f30e00, 0x00000000, 0x3af50e00,
+       0x00000000, 0x7a740000, 0x0000000f, 0x43680010, 0x00000001, 0x83860e00,
+       0x00000000, 0x83060e00, 0x00000003, 0xf4680c00, 0x00000000, 0x286d0000,
+       0x00000000, 0x03690000, 0x00000010, 0xb1f60c54, 0x00000000, 0x0a6a0000,
+       0x00000000, 0x0aeb0000, 0x00000009, 0x0b6cffff, 0x00000000, 0x0c000000,
+       0x00000000, 0x0be90000, 0x00000003, 0x8cf6a000, 0x0000000c, 0x09800002,
+       0x00000010, 0x009f0000, 0x00000010, 0xb8173209, 0x00000000, 0x35140000,
+       0x00000000, 0x35950000, 0x00000005, 0x766c2c00, 0x00000000, 0x34970000,
+       0x00000004, 0xb8f12e00, 0x00000010, 0x001f0000, 0x00000008, 0x0f800004,
+       0x00000018, 0x8000fff7, 0x00000000, 0x03e90000, 0x00000010, 0xb8f6a01a,
+       0x00000010, 0x20130019, 0x00000010, 0xb1f10e18, 0x00000000, 0x83973200,
+       0x00000000, 0x38700e00, 0x00000000, 0xbb760e00, 0x00000000, 0x37d00000,
+       0x0000000c, 0x73e7001a, 0x00000003, 0xb8f1a000, 0x00000000, 0x32140000,
+       0x00000000, 0x32950000, 0x00000005, 0x73e72c00, 0x00000000, 0x33190000,
+       0x00000005, 0x74680000, 0x00000010, 0x0ce7000d, 0x00000008, 0x22000009,
+       0x00000000, 0x07520000, 0x00000000, 0x29000000, 0x0000000c, 0x73e70019,
+       0x0000000f, 0x65680010, 0x0000000c, 0x21420004, 0x00000018, 0x8000003c,
+       0x00000010, 0x20530000, 0x0000000c, 0x61420004, 0x00000000, 0x290e0000,
+       0x00000018, 0x80000002, 0x00000010, 0x91973206, 0x00000000, 0x35140000,
+       0x00000000, 0x35950000, 0x00000005, 0x766c2c00, 0x00000000, 0x34990000,
+       0x00000004, 0xb8f13200, 0x00000000, 0x83690c00, 0x00000010, 0xb1860013,
+       0x00000000, 0x28e90000, 0x00000008, 0x22000004, 0x00000000, 0x23ec0000,
+       0x00000000, 0x03690000, 0x00000010, 0xb8660c07, 0x00000009, 0x036cffff,
+       0x00000000, 0x326a0000, 0x00000000, 0x32eb0000, 0x00000005, 0x73e70c00,
+       0x00000000, 0x33690000, 0x00000005, 0x74680000, 0x0000000c, 0x73e7001c,
+       0x00000000, 0x03690000, 0x00000010, 0xb1f60c12, 0x00000010, 0xb1d00c11,
+       0x0000000c, 0x21420005, 0x0000000c, 0x33e7001c, 0x00000018, 0x8000000e,
+       0x00000010, 0x2e67000d, 0x00000000, 0x03690000, 0x00000010, 0xb1f60c0b,
+       0x00000010, 0xb1d00c0a, 0x00000000, 0x03440000, 0x00000008, 0x2200000c,
+       0x00000000, 0x07520000, 0x00000000, 0x29000000, 0x00000018, 0x80000015,
+       0x0000000c, 0x33e7001c, 0x00000010, 0x20530000, 0x00000000, 0x22060000,
+       0x00000000, 0x290e0000, 0x00000018, 0x000d0000, 0x00000000, 0x06820000,
+       0x00000010, 0x2de7000d, 0x00000010, 0x0ce7000c, 0x00000000, 0x27f20000,
+       0x00000010, 0xb96d9e0a, 0x00000000, 0xa86d9e00, 0x00000009, 0x0361ffff,
+       0x00000010, 0xb7500c07, 0x00000008, 0x2200000f, 0x0000000f, 0x65680010,
+       0x00000000, 0x29000000, 0x00000018, 0x80000004, 0x0000000c, 0x33e7001b,
+       0x00000010, 0x20530000, 0x00000018, 0x000d0000, 0x00000000, 0x2b820000,
+       0x00000010, 0x20d2002f, 0x00000010, 0x0052002e, 0x00000009, 0x054e0007,
+       0x00000010, 0xb18a002c, 0x00000000, 0x050a8c00, 0x00000008, 0x850a0008,
+       0x00000010, 0x918a0029, 0x00000003, 0xc5008800, 0x00000008, 0xa3460001,
+       0x00000010, 0xb1c60007, 0x00000008, 0x22000001, 0x0000000c, 0x29800000,
+       0x00000010, 0x20530000, 0x00000000, 0x274e8c00, 0x00000000, 0x66cd0000,
+       0x00000000, 0x22c58c00, 0x00000008, 0x22000014, 0x00000003, 0x22c58e00,
+       0x00000003, 0x23c58e00, 0x00000003, 0x22c58e00, 0x00000003, 0x26cd9e00,
+       0x00000003, 0x27cd9e00, 0x00000003, 0x26cd9e00, 0x00000003, 0x274ea000,
+       0x00000003, 0x284ea000, 0x00000003, 0x274ea000, 0x0000000c, 0x69520000,
+       0x0000000c, 0x29800000, 0x00000010, 0x20530000, 0x00000003, 0x22c58e00,
+       0x00000003, 0x23c58e00, 0x00000003, 0x22c58e00, 0x00000003, 0x26cd9e00,
+       0x00000003, 0x27cd9e00, 0x00000003, 0x26cd9e00, 0x00000003, 0x274ea000,
+       0x00000003, 0x284ea000, 0x00000003, 0x274ea000, 0x00000000, 0xa2c58c00,
+       0x00000000, 0xa74e8c00, 0x00000000, 0xe6cd0000, 0x0000000f, 0x620a0010,
+       0x00000008, 0x23460001, 0x0000000c, 0x29800000, 0x00000010, 0x20530000,
+       0x0000000c, 0x29520000, 0x00000018, 0x80000002, 0x0000000c, 0x29800000,
+       0x00000018, 0x00570000 };
+
+static int bnx2_TPAT_b06FwReleaseMajor = 0x0;
+static int bnx2_TPAT_b06FwReleaseMinor = 0x0;
+static int bnx2_TPAT_b06FwReleaseFix = 0x0;
+static u32 bnx2_TPAT_b06FwStartAddr = 0x08000858;
+static u32 bnx2_TPAT_b06FwTextAddr = 0x08000800;
+static int bnx2_TPAT_b06FwTextLen = 0x1314;
+static u32 bnx2_TPAT_b06FwDataAddr = 0x08001b40;
+static int bnx2_TPAT_b06FwDataLen = 0x0;
+static u32 bnx2_TPAT_b06FwRodataAddr = 0x00000000;
+static int bnx2_TPAT_b06FwRodataLen = 0x0;
+static u32 bnx2_TPAT_b06FwBssAddr = 0x08001b90;
+static int bnx2_TPAT_b06FwBssLen = 0x80;
+static u32 bnx2_TPAT_b06FwSbssAddr = 0x08001b40;
+static int bnx2_TPAT_b06FwSbssLen = 0x48;
+
+static u32 bnx2_TPAT_b06FwText[(0x1314/4) + 1] = {
+       0x0a000216, 0x00000000, 0x00000000, 0x0000000d, 0x74706174, 0x20302e36,
+       0x2e390000, 0x00060901, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x10000003,
+       0x00000000, 0x0000000d, 0x0000000d, 0x3c020800, 0x24421b40, 0x3c030800,
+       0x24631c10, 0xac400000, 0x0043202b, 0x1480fffd, 0x24420004, 0x3c1d0800,
+       0x37bd3ffc, 0x03a0f021, 0x3c100800, 0x26100858, 0x3c1c0800, 0x279c1b40,
+       0x0e00051f, 0x00000000, 0x0000000d, 0x8f820024, 0x27bdffe8, 0xafbf0014,
+       0x10400004, 0xafb00010, 0x0000000d, 0x00000000, 0x2400015f, 0x8f82001c,
+       0x8c450008, 0x24030800, 0xaf430178, 0x97430104, 0x3c020008, 0xaf420140,
+       0x8f820034, 0x30420001, 0x10400006, 0x3070ffff, 0x24020002, 0x2603fffe,
+       0xa7420146, 0x0a000246, 0xa7430148, 0xa7400146, 0x8f850034, 0x30a20020,
+       0x0002102b, 0x00021023, 0x30460009, 0x30a30c00, 0x24020400, 0x14620002,
+       0x34c40001, 0x34c40005, 0xa744014a, 0x3c020800, 0x8c440820, 0x3c030048,
+       0x24020002, 0x00832025, 0x30a30006, 0x1062000d, 0x2c620003, 0x50400005,
+       0x24020004, 0x10600012, 0x3c020001, 0x0a000271, 0x00000000, 0x10620007,
+       0x24020006, 0x1462000f, 0x3c020111, 0x0a000269, 0x00821025, 0x0a000268,
+       0x3c020101, 0x3c020011, 0x00821025, 0x24030001, 0xaf421000, 0xaf830030,
+       0x0a000271, 0x00000000, 0x00821025, 0xaf421000, 0xaf800030, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x8f830030, 0x1060003f, 0x3c048000,
+       0x8f421000, 0x00441024, 0x1040fffd, 0x00000000, 0x10600039, 0x00000000,
+       0x8f421000, 0x3c030020, 0x00431024, 0x10400034, 0x00000000, 0x97421014,
+       0x14400031, 0x00000000, 0x97421008, 0x8f84001c, 0x24420006, 0x00024082,
+       0x00081880, 0x00643821, 0x8ce50000, 0x30430003, 0x30420001, 0x10400004,
+       0x00000000, 0x0000000d, 0x0a0002b0, 0x00081080, 0x5460000f, 0x30a5ffff,
+       0x3c06ffff, 0x00a62824, 0x0005182b, 0x00a61026, 0x0002102b, 0x00621824,
+       0x10600004, 0x00000000, 0x0000000d, 0x00000000, 0x240001fc, 0x8ce20000,
+       0x0a0002af, 0x00462825, 0x0005182b, 0x38a2ffff, 0x0002102b, 0x00621824,
+       0x10600004, 0x00000000, 0x0000000d, 0x00000000, 0x24000206, 0x8ce20000,
+       0x3445ffff, 0x00081080, 0x00441021, 0x3c030800, 0xac450000, 0x8c620840,
+       0x24420001, 0xac620840, 0x8f820008, 0x10400003, 0x00000000, 0x0e000660,
+       0x00000000, 0x8f840028, 0x02002821, 0x24820008, 0x30421fff, 0x24434000,
+       0x0343d821, 0x30a30007, 0xaf840018, 0xaf820028, 0xaf420084, 0x10600002,
+       0x24a20007, 0x3045fff8, 0x8f820044, 0x8f840004, 0x00451821, 0xaf82002c,
+       0x0064102b, 0xaf830044, 0x14400002, 0x00641023, 0xaf820044, 0x8f840044,
+       0x34028000, 0x8fbf0014, 0x8fb00010, 0x00821021, 0x03421821, 0x3c021000,
+       0xaf83001c, 0xaf440080, 0xaf420178, 0x03e00008, 0x27bd0018, 0x8f820024,
+       0x27bdffe8, 0xafbf0014, 0x10400004, 0xafb00010, 0x0000000d, 0x00000000,
+       0x24000249, 0x8f85001c, 0x24020001, 0xaf820024, 0x8ca70008, 0xa3800023,
+       0x8f620004, 0x3c100800, 0x26041b90, 0x00021402, 0xa3820010, 0x304600ff,
+       0x24c60005, 0x0e00064a, 0x00063082, 0x8f640004, 0x8f430108, 0x3c021000,
+       0x00621824, 0xa7840020, 0x10600008, 0x00000000, 0x97420104, 0x93830023,
+       0x2442ffec, 0x34630002, 0xa3830023, 0x0a000304, 0x3045ffff, 0x97420104,
+       0x2442fff0, 0x3045ffff, 0x8f620004, 0x3042ffff, 0x2c420013, 0x14400004,
+       0x00000000, 0x93820023, 0x34420001, 0xa3820023, 0x93830023, 0x24020001,
+       0x10620009, 0x28620002, 0x14400014, 0x24020002, 0x10620012, 0x24020003,
+       0x1062000a, 0x00000000, 0x0a000325, 0x00000000, 0x8f82001c, 0x8c43000c,
+       0x3c04ffff, 0x00641824, 0x00651825, 0x0a000325, 0xac43000c, 0x8f82001c,
+       0x8c430010, 0x3c04ffff, 0x00641824, 0x00651825, 0xac430010, 0x8f620004,
+       0x3042ffff, 0x24420002, 0x00021083, 0xa3820038, 0x304500ff, 0x8f82001c,
+       0x3c04ffff, 0x00052880, 0x00a22821, 0x8ca70000, 0x97820020, 0x97430104,
+       0x00e42024, 0x24420002, 0x00621823, 0x00833825, 0xaca70000, 0x93840038,
+       0x26061b90, 0x00041080, 0x00461021, 0x90430000, 0x3063000f, 0x00832021,
+       0xa3840022, 0x308200ff, 0x3c04fff6, 0x24420003, 0x00021080, 0x00461021,
+       0x8c450000, 0x93830022, 0x8f82001c, 0x3484ffff, 0x00a43824, 0x00031880,
+       0x00621821, 0xaf850000, 0xac67000c, 0x93820022, 0x93830022, 0x8f84001c,
+       0x24420003, 0x00021080, 0x00461021, 0x24630004, 0x00031880, 0xac470000,
+       0x93820022, 0x00661821, 0x94670002, 0x00021080, 0x00441021, 0xac670000,
+       0x24030010, 0xac470010, 0xa7430140, 0x24030002, 0xa7400142, 0xa7400144,
+       0xa7430146, 0x97420104, 0x8f840034, 0x24030001, 0x2442fffe, 0x30840006,
+       0xa7420148, 0x24020002, 0xa743014a, 0x1082000d, 0x2c820003, 0x10400005,
+       0x24020004, 0x10800011, 0x3c020009, 0x0a000383, 0x00000000, 0x10820007,
+       0x24020006, 0x1482000d, 0x3c020119, 0x0a00037d, 0x24030001, 0x0a00037c,
+       0x3c020109, 0x3c020019, 0x24030001, 0xaf421000, 0xaf830030, 0x0a000383,
+       0x00000000, 0xaf421000, 0xaf800030, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x93820010, 0x24030008, 0x8f840030, 0x24420002, 0x30420007,
+       0x00621823, 0x30630007, 0xaf83000c, 0x10800005, 0x3c038000, 0x8f421000,
+       0x00431024, 0x1040fffd, 0x00000000, 0x8f820028, 0xaf820018, 0x24420010,
+       0x30421fff, 0xaf820028, 0xaf420084, 0x97430104, 0x24424000, 0x0342d821,
+       0x3063ffff, 0x30620007, 0x10400002, 0x24620007, 0x3043fff8, 0x8f820044,
+       0x8f840004, 0x00431821, 0xaf82002c, 0x0064102b, 0xaf830044, 0x14400002,
+       0x00641023, 0xaf820044, 0x8f840044, 0x34028000, 0x8fbf0014, 0x8fb00010,
+       0x00821021, 0x03421821, 0x3c021000, 0xaf83001c, 0xaf440080, 0xaf420178,
+       0x03e00008, 0x27bd0018, 0x8f820024, 0x27bdffe8, 0xafbf0014, 0x14400004,
+       0xafb00010, 0x0000000d, 0x00000000, 0x240002db, 0x8f620004, 0x04410009,
+       0x3c050800, 0x93820022, 0x8f830000, 0x24a41b90, 0xaf800024, 0x24420003,
+       0x00021080, 0x00441021, 0xac430000, 0x93820038, 0x24a51b90, 0x93860010,
+       0x3c040001, 0x27700008, 0x24420001, 0x00021080, 0x00451021, 0x8c430000,
+       0x24c60005, 0x00063082, 0x00641821, 0x02002021, 0x0e00064a, 0xac430000,
+       0x93840022, 0x3c057fff, 0x8f620004, 0x00042080, 0x00902021, 0x8c830004,
+       0x34a5ffff, 0x00451024, 0x00621821, 0xac830004, 0x93850038, 0x3c07ffff,
+       0x93840010, 0x00052880, 0x00b02821, 0x8ca30000, 0x97420104, 0x97860020,
+       0x00671824, 0x00441021, 0x00461023, 0x3042ffff, 0x00621825, 0xaca30000,
+       0x93830023, 0x24020001, 0x10620009, 0x28620002, 0x1440001a, 0x24020002,
+       0x10620018, 0x24020003, 0x1062000d, 0x00000000, 0x0a000411, 0x00000000,
+       0x93820010, 0x97430104, 0x8e04000c, 0x00621821, 0x2463fff2, 0x3063ffff,
+       0x00872024, 0x00832025, 0x0a000411, 0xae04000c, 0x93820010, 0x97430104,
+       0x8e040010, 0x00621821, 0x2463ffee, 0x3063ffff, 0x00872024, 0x00832025,
+       0xae040010, 0x9783000e, 0x8f840034, 0x2402000a, 0xa7420140, 0xa7430142,
+       0x93820010, 0xa7420144, 0xa7400146, 0x97430104, 0x30840006, 0x24020001,
+       0xa7430148, 0xa742014a, 0x24020002, 0x1082000d, 0x2c820003, 0x10400005,
+       0x24020004, 0x10800011, 0x3c020041, 0x0a000437, 0x00000000, 0x10820007,
+       0x24020006, 0x1482000d, 0x3c020151, 0x0a000431, 0x24030001, 0x0a000430,
+       0x3c020141, 0x3c020051, 0x24030001, 0xaf421000, 0xaf830030, 0x0a000437,
+       0x00000000, 0xaf421000, 0xaf800030, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x8f820030, 0x93840010, 0x8f850028, 0x10400005, 0x3c038000,
+       0x8f421000, 0x00431024, 0x1040fffd, 0x00000000, 0x2483000a, 0x30620007,
+       0x10400002, 0x24620007, 0x304303f8, 0x00a31021, 0x30421fff, 0xaf850018,
+       0xaf820028, 0xaf420084, 0x97430104, 0x24424000, 0x0342d821, 0x3063ffff,
+       0x30620007, 0x10400002, 0x24620007, 0x3043fff8, 0x8f820044, 0x8f840004,
+       0x00431821, 0xaf82002c, 0x0064102b, 0xaf830044, 0x14400002, 0x00641023,
+       0xaf820044, 0x8f840044, 0x34028000, 0x8fbf0014, 0x8fb00010, 0x00821021,
+       0x03421821, 0x3c021000, 0xaf83001c, 0xaf440080, 0xaf420178, 0x03e00008,
+       0x27bd0018, 0x3c026000, 0x8c444448, 0x3c030800, 0xac64082c, 0x8f620000,
+       0x97430104, 0x3c048000, 0x3046ffff, 0x3067ffff, 0x8f420178, 0x00441024,
+       0x1440fffd, 0x2402000a, 0x30c30007, 0xa7420140, 0x24020008, 0x00431023,
+       0x30420007, 0x24c3fffe, 0xa7420142, 0xa7430144, 0xa7400146, 0xa7470148,
+       0x8f420108, 0x3c036000, 0x8f850034, 0x30420020, 0x0002102b, 0x00021023,
+       0x30420009, 0x34420001, 0xa742014a, 0x8c644448, 0x3c020800, 0x30a50006,
+       0xac440830, 0x24020002, 0x10a2000d, 0x2ca20003, 0x10400005, 0x24020004,
+       0x10a00011, 0x3c020041, 0x0a0004a8, 0x00000000, 0x10a20007, 0x24020006,
+       0x14a2000d, 0x3c020151, 0x0a0004a2, 0x24030001, 0x0a0004a1, 0x3c020141,
+       0x3c020051, 0x24030001, 0xaf421000, 0xaf830030, 0x0a0004a8, 0x00000000,
+       0xaf421000, 0xaf800030, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x8f820030, 0x24c30008, 0x10400006, 0x30e6ffff, 0x3c048000, 0x8f421000,
+       0x00441024, 0x1040fffd, 0x00000000, 0x3c026000, 0x8c444448, 0x3065ffff,
+       0x3c020800, 0x30a30007, 0x10600003, 0xac440834, 0x24a20007, 0x3045fff8,
+       0x8f840028, 0x00851021, 0x30421fff, 0x24434000, 0x0343d821, 0x30c30007,
+       0xaf840018, 0xaf820028, 0xaf420084, 0x10600002, 0x24c20007, 0x3046fff8,
+       0x8f820044, 0x8f840004, 0x00461821, 0xaf82002c, 0x0064102b, 0xaf830044,
+       0x14400002, 0x00641023, 0xaf820044, 0x8f840044, 0x34028000, 0x3c030800,
+       0x8c650844, 0x00821021, 0x03421821, 0xaf83001c, 0xaf440080, 0x10a00006,
+       0x2402000e, 0x93830043, 0x14620004, 0x3c021000, 0x2402043f, 0xa7420148,
+       0x3c021000, 0x3c036000, 0xaf420178, 0x8c644448, 0x3c020800, 0x03e00008,
+       0xac440838, 0x8f820034, 0x30424000, 0x10400005, 0x24020800, 0x0000000d,
+       0x00000000, 0x24000405, 0x24020800, 0xaf420178, 0x97440104, 0x3c030008,
+       0xaf430140, 0x8f820034, 0x30420001, 0x10400006, 0x3085ffff, 0x24020002,
+       0x24a3fffe, 0xa7420146, 0x0a0004ff, 0xa7430148, 0xa7400146, 0x8f840028,
+       0x2402000d, 0xa742014a, 0x24830008, 0x30631fff, 0x24624000, 0x0342d821,
+       0x30a20007, 0xaf840018, 0xaf830028, 0xaf430084, 0x10400002, 0x24a20007,
+       0x3045fff8, 0x8f820044, 0x8f840004, 0x00451821, 0xaf82002c, 0x0064102b,
+       0xaf830044, 0x14400002, 0x00641023, 0xaf820044, 0x8f840044, 0x34028000,
+       0x00821021, 0x03421821, 0x3c021000, 0xaf83001c, 0xaf440080, 0x03e00008,
+       0xaf420178, 0x27bdffe8, 0x3c046008, 0xafbf0014, 0xafb00010, 0x8c825000,
+       0x3c1a8000, 0x2403ff7f, 0x375b4000, 0x00431024, 0x3442380c, 0xac825000,
+       0x8f430008, 0x3c100800, 0x37428000, 0x34630001, 0xaf430008, 0xaf82001c,
+       0x3c02601c, 0xaf800028, 0xaf400080, 0xaf400084, 0x8c450008, 0x3c036000,
+       0x8c620808, 0x3c040800, 0x3c030080, 0xac830820, 0x3042fff0, 0x38420010,
+       0x2c420001, 0xaf850004, 0xaf820008, 0x0e00062f, 0x00000000, 0x8f420000,
+       0x30420001, 0x1040fffb, 0x00000000, 0x8f440108, 0x30822000, 0xaf840034,
+       0x10400004, 0x8e02083c, 0x24420001, 0x0a00059d, 0xae02083c, 0x30820200,
+       0x10400027, 0x00000000, 0x97420104, 0x1040001c, 0x30824000, 0x14400005,
+       0x00000000, 0x0e00022d, 0x00000000, 0x0a000592, 0x00000000, 0x8f620008,
+       0x8f630000, 0x24020030, 0x00031e02, 0x306300f0, 0x10620007, 0x28620031,
+       0x14400031, 0x24020040, 0x10620007, 0x00000000, 0x0a000592, 0x00000000,
+       0x0e0002dd, 0x00000000, 0x0a000592, 0x00000000, 0x0e0003b8, 0x00000000,
+       0x0a000592, 0x00000000, 0x30820040, 0x1440002d, 0x00000000, 0x0000000d,
+       0x00000000, 0x240004a6, 0x0a00059d, 0x00000000, 0x8f430100, 0x24020d00,
+       0x1462000f, 0x30820006, 0x97420104, 0x10400005, 0x30820040, 0x0e0004e9,
+       0x00000000, 0x0a000592, 0x00000000, 0x1440001b, 0x00000000, 0x0000000d,
+       0x00000000, 0x240004b8, 0x0a00059d, 0x00000000, 0x1040000e, 0x30821000,
+       0x10400005, 0x00000000, 0x0e00065d, 0x00000000, 0x0a000592, 0x00000000,
+       0x0e00046b, 0x00000000, 0x8f820040, 0x24420001, 0xaf820040, 0x0a00059d,
+       0x00000000, 0x30820040, 0x14400004, 0x00000000, 0x0000000d, 0x00000000,
+       0x240004cf, 0x8f420138, 0x3c034000, 0x00431025, 0xaf420138, 0x0a00053f,
+       0x00000000, 0x3c046008, 0x8c835000, 0x3c1a8000, 0x2402ff7f, 0x375b4000,
+       0x00621824, 0x3463380c, 0xac835000, 0x8f420008, 0x3c056000, 0x3c03601c,
+       0x34420001, 0xaf420008, 0x37428000, 0xaf800028, 0xaf82001c, 0xaf400080,
+       0xaf400084, 0x8c660008, 0x8ca20808, 0x3c040800, 0x3c030080, 0xac830820,
+       0x3042fff0, 0x38420010, 0x2c420001, 0xaf860004, 0xaf820008, 0x03e00008,
+       0x00000000, 0x3084ffff, 0x30820007, 0x10400002, 0x24820007, 0x3044fff8,
+       0x8f820028, 0x00441821, 0x30631fff, 0x24644000, 0x0344d821, 0xaf820018,
+       0xaf830028, 0x03e00008, 0xaf430084, 0x3084ffff, 0x30820007, 0x10400002,
+       0x24820007, 0x3044fff8, 0x8f820044, 0x8f830004, 0x00442021, 0xaf82002c,
+       0x0083102b, 0xaf840044, 0x14400002, 0x00831023, 0xaf820044, 0x8f820044,
+       0x34038000, 0x00431821, 0x03432021, 0xaf84001c, 0x03e00008, 0xaf420080,
+       0x8f830034, 0x24020002, 0x30630006, 0x1062000d, 0x2c620003, 0x50400005,
+       0x24020004, 0x10600012, 0x3c020001, 0x0a000601, 0x00000000, 0x10620007,
+       0x24020006, 0x1462000f, 0x3c020111, 0x0a0005f9, 0x00821025, 0x0a0005f8,
+       0x3c020101, 0x3c020011, 0x00821025, 0x24030001, 0xaf421000, 0xaf830030,
+       0x0a000601, 0x00000000, 0x00821025, 0xaf421000, 0xaf800030, 0x00000000,
+       0x00000000, 0x00000000, 0x03e00008, 0x00000000, 0x8f820030, 0x10400005,
+       0x3c038000, 0x8f421000, 0x00431024, 0x1040fffd, 0x00000000, 0x03e00008,
+       0x00000000, 0x8f820034, 0x27bdffe8, 0x30424000, 0x14400005, 0xafbf0010,
+       0x0e00022d, 0x00000000, 0x0a00062d, 0x8fbf0010, 0x8f620008, 0x8f630000,
+       0x24020030, 0x00031e02, 0x306300f0, 0x10620008, 0x28620031, 0x1440000d,
+       0x8fbf0010, 0x24020040, 0x10620007, 0x00000000, 0x0a00062d, 0x00000000,
+       0x0e0002dd, 0x00000000, 0x0a00062d, 0x8fbf0010, 0x0e0003b8, 0x00000000,
+       0x8fbf0010, 0x03e00008, 0x27bd0018, 0x8f84003c, 0x1080000f, 0x3c026000,
+       0x8c430c3c, 0x30630fff, 0xaf830014, 0x14600011, 0x3082000f, 0x10400005,
+       0x308200f0, 0x10400003, 0x30820f00, 0x14400006, 0x00000000, 0x0000000d,
+       0x00000000, 0x2400050e, 0x03e00008, 0x00000000, 0x0000000d, 0x00000000,
+       0x24000513, 0x03e00008, 0x00000000, 0xaf83003c, 0x03e00008, 0x00000000,
+       0x10c00007, 0x00000000, 0x8ca20000, 0x24c6ffff, 0x24a50004, 0xac820000,
+       0x14c0fffb, 0x24840004, 0x03e00008, 0x00000000, 0x0a000659, 0x00a01021,
+       0xac860000, 0x24840004, 0x00a01021, 0x1440fffc, 0x24a5ffff, 0x03e00008,
+       0x00000000, 0x0000000d, 0x03e00008, 0x00000000, 0x3c040800, 0x8c82084c,
+       0x54400007, 0xac80084c, 0x8f820034, 0x24030400, 0x30420c00, 0x1443005b,
+       0x00000000, 0xac80084c, 0x0000000d, 0x00000000, 0x2400003c, 0x3c026000,
+       0x8c444448, 0x3c030800, 0xac640850, 0x24000043, 0x97420104, 0x3045ffff,
+       0x000530c2, 0x24a2007f, 0x000239c2, 0x2400004e, 0x3c046020, 0x24030020,
+       0xac830000, 0x8c820000, 0x30420020, 0x10400005, 0x3c036020, 0x8c620000,
+       0x30420020, 0x1440fffd, 0x00000000, 0x3c026020, 0x8c430010, 0x24040001,
+       0x0087102b, 0x30ea007f, 0x24abfffe, 0x10400010, 0x00034240, 0x3c056020,
+       0x24090020, 0xaca90000, 0x8ca20000, 0x30420020, 0x10400006, 0x24840001,
+       0x3c036020, 0x8c620000, 0x30420020, 0x1440fffd, 0x00000000, 0x0087102b,
+       0x1440fff4, 0x00000000, 0x8f85001c, 0x3c026020, 0x8c430010, 0x3c046020,
+       0x34848000, 0x006a1825, 0x01034025, 0x2400006b, 0x10c0000b, 0x00000000,
+       0x8ca30000, 0x24a50004, 0x8ca20000, 0x24a50004, 0x24c6ffff, 0xac820000,
+       0x24840004, 0xac830000, 0x14c0fff7, 0x24840004, 0x24000077, 0x3c020007,
+       0x34427700, 0x3c036000, 0xac6223c8, 0xac6b23cc, 0xac6823e4, 0x24000086,
+       0x3c046000, 0x3c038000, 0x8c8223f8, 0x00431024, 0x1440fffd, 0x3c021000,
+       0x3c056000, 0x24030019, 0xaca223f8, 0xa743014a, 0x8ca44448, 0x3c020800,
+       0xac440854, 0x03e00008, 0x00000000, 0x00000000 };
+
+static u32 bnx2_TPAT_b06FwData[(0x0/4) + 1] = { 0x00000000 };
+static u32 bnx2_TPAT_b06FwRodata[(0x0/4) + 1] = { 0x00000000 };
+static u32 bnx2_TPAT_b06FwBss[(0x80/4) + 1] = { 0x00000000 };
+static u32 bnx2_TPAT_b06FwSbss[(0x48/4) + 1] = { 0x00000000 };
+
+static int bnx2_TXP_b06FwReleaseMajor = 0x0;
+static int bnx2_TXP_b06FwReleaseMinor = 0x0;
+static int bnx2_TXP_b06FwReleaseFix = 0x0;
+static u32 bnx2_TXP_b06FwStartAddr = 0x08002090;
+static u32 bnx2_TXP_b06FwTextAddr = 0x08000000;
+static int bnx2_TXP_b06FwTextLen = 0x3ffc;
+static u32 bnx2_TXP_b06FwDataAddr = 0x08004020;
+static int bnx2_TXP_b06FwDataLen = 0x0;
+static u32 bnx2_TXP_b06FwRodataAddr = 0x00000000;
+static int bnx2_TXP_b06FwRodataLen = 0x0;
+static u32 bnx2_TXP_b06FwBssAddr = 0x08004060;
+static int bnx2_TXP_b06FwBssLen = 0x194;
+static u32 bnx2_TXP_b06FwSbssAddr = 0x08004020;
+static int bnx2_TXP_b06FwSbssLen = 0x34;
+static u32 bnx2_TXP_b06FwText[(0x3ffc/4) + 1] = {
+       0x0a000824, 0x00000000, 0x00000000, 0x0000000d, 0x74787020, 0x302e362e,
+       0x39000000, 0x00060900, 0x0000000a, 0x000003e8, 0x0000ea60, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x00000000, 0x10000003, 0x00000000, 0x0000000d,
+       0x0000000d, 0x3c020800, 0x24424020, 0x3c030800, 0x246341f4, 0xac400000,
+       0x0043202b, 0x1480fffd, 0x24420004, 0x3c1d0800, 0x37bd7ffc, 0x03a0f021,
+       0x3c100800, 0x26102090, 0x3c1c0800, 0x279c4020, 0x0e000a0e, 0x00000000,
+       0x0000000d, 0x8f840014, 0x27bdffe8, 0xafb00010, 0x8f460104, 0x8f830008,
+       0x8c8500ac, 0xaf430080, 0x948200a8, 0xa7420e10, 0x948300aa, 0xa7430e12,
+       0x8c8200ac, 0xaf420e18, 0x97430e10, 0xa7430e14, 0x97420e12, 0xa7420e16,
+       0x8f430e18, 0x00005021, 0x00c53023, 0x10c001a3, 0xaf430e1c, 0x240f0800,
+       0x3c0e1000, 0x2419fff8, 0x24100010, 0x3c188100, 0x93620008, 0x10400009,
+       0x00000000, 0x97620010, 0x00c2102b, 0x14400005, 0x00000000, 0x97620010,
+       0x3042ffff, 0x0a000862, 0xaf420e00, 0xaf460e00, 0x8f420000, 0x30420008,
+       0x1040fffd, 0x00000000, 0x97420e08, 0x8f450e04, 0x3044ffff, 0x30820001,
+       0x14400005, 0x00000000, 0x14a00005, 0x3083a040, 0x0a0009e6, 0x00000000,
+       0x0000000d, 0x3083a040, 0x24020040, 0x14620049, 0x3082a000, 0x8f87000c,
+       0x30880036, 0x30890008, 0xaf4f0178, 0x00e01821, 0x9742008a, 0x00431023,
+       0x2442ffff, 0x30421fff, 0x2c420008, 0x1440fffa, 0x00000000, 0x8f830018,
+       0x00a05021, 0x00c53023, 0x24e24000, 0x03422821, 0x306b00ff, 0x24630001,
+       0xaf830018, 0x93840012, 0x000b1400, 0x3c030100, 0x00431025, 0xaca20000,
+       0x8f820018, 0x30840007, 0x00042240, 0x34870001, 0x00e83825, 0x1120000f,
+       0xaca20004, 0x97430e0a, 0x8f84000c, 0x00ee3825, 0x2402000e, 0x00781825,
+       0xaf430160, 0x25430006, 0x24840008, 0x30841fff, 0xa742015a, 0xa7430158,
+       0xaf84000c, 0x0a0008a9, 0x00000000, 0x8f83000c, 0x25420002, 0xa7420158,
+       0x24630008, 0x30631fff, 0xaf83000c, 0x54c0000c, 0x8f420e14, 0x97420e10,
+       0x97430e12, 0x8f840014, 0x00021400, 0x00621825, 0xac8300a8, 0x8f850014,
+       0x8f420e18, 0x34e70040, 0xaca200ac, 0x8f420e14, 0x8f430e1c, 0xaf420144,
+       0xaf430148, 0xa34b0152, 0xaf470154, 0x0a0009f1, 0xaf4e0178, 0x10400128,
+       0x00000000, 0x97620010, 0x00a2102b, 0x10400003, 0x30820040, 0x10400122,
+       0x00000000, 0xafa60008, 0xa7840010, 0xaf850004, 0x93620008, 0x1440005e,
+       0x27ac0008, 0xaf60000c, 0x97820010, 0x30424000, 0x10400002, 0x2403000e,
+       0x24030016, 0xa363000a, 0x24034007, 0xaf630014, 0x93820012, 0x8f630014,
+       0x30420007, 0x00021240, 0x00621825, 0xaf630014, 0x97820010, 0x8f630014,
+       0x30420010, 0x00621825, 0xaf630014, 0x97820010, 0x30420008, 0x5040000e,
+       0x00002821, 0x8f620014, 0x004e1025, 0xaf620014, 0x97430e0a, 0x2402000e,
+       0x00781825, 0xaf630004, 0xa3620002, 0x9363000a, 0x3405fffc, 0x24630004,
+       0x0a0008f2, 0xa363000a, 0xaf600004, 0xa3600002, 0x97820010, 0x9363000a,
+       0x30421f00, 0x00021182, 0x24420028, 0x00621821, 0xa3630009, 0x97420e0c,
+       0xa7620010, 0x93630009, 0x24020008, 0x24630002, 0x30630007, 0x00431023,
+       0x30420007, 0xa362000b, 0x93640009, 0x97620010, 0x8f890004, 0x97830010,
+       0x00441021, 0x00a21021, 0x30630040, 0x10600006, 0x3045ffff, 0x15250005,
+       0x0125102b, 0x3c068000, 0x0a000925, 0x00005821, 0x0125102b, 0x144000c8,
+       0x00005021, 0x97420e14, 0xa7420e10, 0x97430e16, 0xa7430e12, 0x8f420e1c,
+       0xaf420e18, 0xaf450e00, 0x8f420000, 0x30420008, 0x1040fffd, 0x00000000,
+       0x97420e08, 0x00a04821, 0xa7820010, 0x8f430e04, 0x00003021, 0x240b0001,
+       0xaf830004, 0x97620010, 0x0a000936, 0x304dffff, 0x8f890004, 0x97820010,
+       0x30420040, 0x10400004, 0x01206821, 0x3c068000, 0x0a000936, 0x00005821,
+       0x97630010, 0x8f820004, 0x144300a7, 0x00005021, 0x00003021, 0x240b0001,
+       0x8d820000, 0x00491023, 0x1440000d, 0xad820000, 0x8f620014, 0x34420040,
+       0xaf620014, 0x97430e10, 0x97420e12, 0x8f840014, 0x00031c00, 0x00431025,
+       0xac8200a8, 0x8f830014, 0x8f420e18, 0xac6200ac, 0x93620008, 0x1440003f,
+       0x00000000, 0x25260002, 0x8f84000c, 0x9743008a, 0x3063ffff, 0xafa30000,
+       0x8fa20000, 0x00441023, 0x2442ffff, 0x30421fff, 0x2c420010, 0x1440fff7,
+       0x00000000, 0x8f82000c, 0x8f830018, 0x00021082, 0x00021080, 0x24424000,
+       0x03422821, 0x00605021, 0x24630001, 0x314200ff, 0x00021400, 0xaf830018,
+       0x3c033200, 0x00431025, 0xaca20000, 0x93630009, 0x9362000a, 0x00031c00,
+       0x00431025, 0xaca20004, 0x8f830018, 0xaca30008, 0x97820010, 0x30420008,
+       0x10400002, 0x00c04021, 0x25280006, 0x97430e14, 0x93640002, 0x8f450e1c,
+       0x8f660004, 0x8f670014, 0xaf4f0178, 0x3063ffff, 0xa7430144, 0x97420e16,
+       0xa7420146, 0xaf450148, 0xa34a0152, 0x8f82000c, 0x308400ff, 0xa744015a,
+       0xaf460160, 0xa7480158, 0xaf470154, 0xaf4e0178, 0x00501021, 0x30421fff,
+       0xaf82000c, 0x0a0009c5, 0x8d820000, 0x93620009, 0x9363000b, 0x8f85000c,
+       0x2463000a, 0x00435021, 0x25440007, 0x00992024, 0x9743008a, 0x3063ffff,
+       0xafa30000, 0x8fa20000, 0x00451023, 0x2442ffff, 0x30421fff, 0x0044102b,
+       0x1440fff7, 0x00000000, 0x8f82000c, 0x8f840018, 0x00021082, 0x00021080,
+       0x24424000, 0x03422821, 0x00804021, 0x24840001, 0xaf840018, 0x93630009,
+       0x310200ff, 0x00022400, 0x3c024100, 0x24630002, 0x00621825, 0x00832025,
+       0xaca40000, 0x8f62000c, 0x00461025, 0xaca20004, 0x97430e14, 0x93640002,
+       0x8f450e1c, 0x8f660004, 0x8f670014, 0xaf4f0178, 0x3063ffff, 0xa7430144,
+       0x97420e16, 0x308400ff, 0xa7420146, 0xaf450148, 0xa3480152, 0x8f83000c,
+       0x25420007, 0x00591024, 0xa744015a, 0xaf460160, 0xa7490158, 0xaf470154,
+       0xaf4e0178, 0x00621821, 0x30631fff, 0xaf83000c, 0x8d820000, 0x14400005,
+       0x00000000, 0x8f620014, 0x2403ffbf, 0x00431024, 0xaf620014, 0x8f62000c,
+       0x004d1021, 0xaf62000c, 0x93630008, 0x14600008, 0x00000000, 0x11600006,
+       0x00000000, 0x8f630014, 0x3c02efff, 0x3442fffe, 0x00621824, 0xaf630014,
+       0xa36b0008, 0x01205021, 0x15400016, 0x8fa60008, 0x97420e14, 0x97430e16,
+       0x8f850014, 0x00021400, 0x00621825, 0xaca300a8, 0x8f840014, 0x8f420e1c,
+       0x0a0009f3, 0xac8200ac, 0x97420e14, 0x97430e16, 0x8f840014, 0x00021400,
+       0x00621825, 0xac8300a8, 0x8f850014, 0x8f420e1c, 0x00005021, 0x0a0009f3,
+       0xaca200ac, 0x14c0fe64, 0x00000000, 0x55400018, 0x8fb00010, 0x3c038000,
+       0x8f420178, 0x00431024, 0x1440fffd, 0x00000000, 0x97430e14, 0x8f440e1c,
+       0x24020800, 0xaf420178, 0x3063ffff, 0xa7430144, 0x97420e16, 0x3c031000,
+       0xa7420146, 0x24020240, 0xaf440148, 0xa3400152, 0xa740015a, 0xaf400160,
+       0xa7400158, 0xaf420154, 0xaf430178, 0x8fb00010, 0x03e00008, 0x27bd0018,
+       0x27bdffd8, 0x3c1a8000, 0x3c0420ff, 0x3484fffd, 0x3c020008, 0x03421821,
+       0xafbf0020, 0xafb3001c, 0xafb20018, 0xafb10014, 0xafb00010, 0xaf830014,
+       0xaf440e00, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+       0x3c0200ff, 0x3442fffd, 0x3c046004, 0xaf420e00, 0x8c835000, 0x24130d00,
+       0x3c120800, 0x3c114000, 0x2402ff7f, 0x00621824, 0x3463380c, 0x24020009,
+       0xac835000, 0xaf420008, 0xaf800018, 0xaf80000c, 0x0e000fa1, 0x00000000,
+       0x0e000a96, 0x00000000, 0x3c020800, 0x24504080, 0x8f420000, 0x30420001,
+       0x1040fffd, 0x00000000, 0x8f440100, 0xaf840008, 0xaf440020, 0x93430108,
+       0xa3830012, 0x93820012, 0x30420001, 0x10400008, 0x00000000, 0x93820012,
+       0x30420006, 0x00021100, 0x0e00083b, 0x0050d821, 0x0a000a52, 0x00000000,
+       0x14930005, 0x00000000, 0x0e00083b, 0x265b4100, 0x0a000a52, 0x00000000,
+       0x0e000ba3, 0x00000000, 0xaf510138, 0x0a000a36, 0x00000000, 0x27bdfff8,
+       0x3084ffff, 0x24820007, 0x3044fff8, 0x8f85000c, 0x9743008a, 0x3063ffff,
+       0xafa30000, 0x8fa20000, 0x00451023, 0x2442ffff, 0x30421fff, 0x0044102b,
+       0x1440fff7, 0x00000000, 0x8f82000c, 0x00021082, 0x00021080, 0x24424000,
+       0x03421021, 0x03e00008, 0x27bd0008, 0x3084ffff, 0x8f82000c, 0x24840007,
+       0x3084fff8, 0x00441021, 0x30421fff, 0xaf82000c, 0x03e00008, 0x00000000,
+       0x27bdffe8, 0x3c1a8000, 0x3c0420ff, 0x3484fffd, 0x3c020008, 0x03421821,
+       0xafbf0010, 0xaf830014, 0xaf440e00, 0x00000000, 0x00000000, 0x00000000,
+       0x00000000, 0x00000000, 0x3c0200ff, 0x3442fffd, 0x3c046004, 0xaf420e00,
+       0x8c825000, 0x2403ff7f, 0x00431024, 0x3442380c, 0x24030009, 0xac825000,
+       0xaf430008, 0xaf800018, 0xaf80000c, 0x0e000fa1, 0x00000000, 0x0e000a96,
+       0x00000000, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x27bdffe8, 0x3c02000a,
+       0x03421821, 0x3c040800, 0x24844120, 0x24050018, 0xafbf0010, 0xaf830024,
+       0x0e000fad, 0x00003021, 0x3c050800, 0x3c020800, 0x24423d60, 0xaca24180,
+       0x24a54180, 0x3c020800, 0x24423e18, 0x3c030800, 0x24633e2c, 0x3c040800,
+       0xaca20004, 0x3c020800, 0x24423d68, 0xaca30008, 0xac824190, 0x24844190,
+       0x3c020800, 0x24423da4, 0x3c070800, 0x24e73de4, 0x3c060800, 0x24c63e40,
+       0x3c050800, 0x24a52b28, 0x3c030800, 0xac820004, 0x3c020800, 0x24423e48,
+       0xac870008, 0xac86000c, 0xac850010, 0xac6241b0, 0x246341b0, 0x8fbf0010,
+       0x3c020800, 0x24423e60, 0xac620004, 0xac670008, 0xac66000c, 0xac650010,
+       0x03e00008, 0x27bd0018, 0x27bdffc8, 0x3c020800, 0x24424120, 0xafbf0030,
+       0xafb3002c, 0xafb20028, 0xafb10024, 0xafb00020, 0x90470021, 0x8c510008,
+       0x8c45001c, 0x8f900020, 0x3c060800, 0x3c038000, 0x8f420178, 0x00431024,
+       0x1440fffd, 0x8cc2414c, 0x24c3414c, 0x2473ffd4, 0xaf420144, 0x8e620030,
+       0x30b22000, 0xaf420148, 0x3c021000, 0xaf50014c, 0xa3470152, 0xa7510158,
+       0xaf450154, 0xaf420178, 0x12400004, 0x3c030800, 0x8c620030, 0x24420001,
+       0xac620030, 0x93420109, 0x9344010a, 0x00111c00, 0xafa30018, 0x00071a00,
+       0xafa50014, 0x8cc5414c, 0x00021600, 0x00042400, 0x00441025, 0x00431025,
+       0xafa20010, 0x8f440100, 0x8e660030, 0x0e000fe1, 0x02003821, 0x1640000e,
+       0x8fbf0030, 0x8f820000, 0x8e630030, 0x8c44017c, 0x02031823, 0x00711823,
+       0x00641823, 0x2c630002, 0x14600006, 0x8fb3002c, 0x0000000d, 0x00000000,
+       0x240000ca, 0x8fbf0030, 0x8fb3002c, 0x8fb20028, 0x8fb10024, 0x8fb00020,
+       0x03e00008, 0x27bd0038, 0x974309da, 0x00804021, 0xad030000, 0x8f4209dc,
+       0xad020004, 0x8f4309e0, 0xad030008, 0x934409d9, 0x24020001, 0x30840003,
+       0x1082001f, 0x30a900ff, 0x28820002, 0x10400005, 0x24020002, 0x10800009,
+       0x3c0a0800, 0x0a000b64, 0x93420934, 0x1082000b, 0x24020003, 0x10820026,
+       0x3c0a0800, 0x0a000b64, 0x93420934, 0x974209e4, 0x00021400, 0x34420800,
+       0xad02000c, 0x0a000b63, 0x25080010, 0x974209e4, 0x00021400, 0x34428100,
+       0xad02000c, 0x974309e8, 0x3c0a0800, 0x00031c00, 0x34630800, 0xad030010,
+       0x0a000b63, 0x25080014, 0x974409e4, 0x3c050800, 0x24a24120, 0x94430018,
+       0x94460010, 0x9447000c, 0x00a05021, 0x24020800, 0xad000010, 0xad020014,
+       0x00042400, 0x00661821, 0x00671823, 0x2463fff2, 0x00832025, 0xad04000c,
+       0x0a000b63, 0x25080018, 0x974209e4, 0x3c050800, 0x00021400, 0x34428100,
+       0xad02000c, 0x974409e8, 0x24a24120, 0x94430018, 0x94460010, 0x9447000c,
+       0x00a05021, 0x24020800, 0xad000014, 0xad020018, 0x00042400, 0x00661821,
+       0x00671823, 0x2463ffee, 0x00832025, 0xad040010, 0x2508001c, 0x93420934,
+       0x93450921, 0x3c074000, 0x25444120, 0x94830014, 0x94860010, 0x00021082,
+       0x00021600, 0x00052c00, 0x00a72825, 0x00451025, 0x00661821, 0x00431025,
+       0xad020000, 0x97830028, 0x974209ea, 0x00621821, 0x00031c00, 0xad030004,
+       0x97820028, 0x24420001, 0x30427fff, 0xa7820028, 0x93430920, 0x3c020006,
+       0x00031e00, 0x00621825, 0xad030008, 0x8f42092c, 0xad02000c, 0x8f430930,
+       0xad030010, 0x8f440938, 0x25080014, 0xad040000, 0x8f820020, 0x11200004,
+       0xad020004, 0x8f420940, 0x0a000b8d, 0x2442ffff, 0x8f420940, 0xad020008,
+       0x8f440948, 0x8f420940, 0x93430936, 0x00822823, 0x00652806, 0x3402ffff,
+       0x0045102b, 0x54400001, 0x3405ffff, 0x93420937, 0x25444120, 0x90830020,
+       0xad000010, 0x00021700, 0x34630010, 0x00031c00, 0x00431025, 0x00451025,
+       0xad02000c, 0x03e00008, 0x25020014, 0x27bdffb0, 0x3c020008, 0x03421821,
+       0xafbf004c, 0xafbe0048, 0xafb70044, 0xafb60040, 0xafb5003c, 0xafb40038,
+       0xafb30034, 0xafb20030, 0xafb1002c, 0xafb00028, 0xaf830000, 0x24020040,
+       0xaf420814, 0xaf400810, 0x8f420944, 0x8f430950, 0x8f440954, 0x8f45095c,
+       0xaf820030, 0xaf830020, 0xaf84001c, 0xaf85002c, 0x93430900, 0x24020020,
+       0x10620005, 0x24020030, 0x10620022, 0x3c030800, 0x0a000bf1, 0x8c62002c,
+       0x24020088, 0xaf420818, 0x3c020800, 0x24424180, 0xafa20020, 0x93430109,
+       0x3c020800, 0x10600009, 0x24574190, 0x3c026000, 0x24030100, 0xac43081c,
+       0x3c030001, 0xac43081c, 0x0000000d, 0x00000000, 0x2400031d, 0x9342010a,
+       0x30420080, 0x1440001c, 0x00000000, 0x3c026000, 0x24030100, 0xac43081c,
+       0x3c030001, 0xac43081c, 0x0000000d, 0x00000000, 0x24000324, 0x0a000bf4,
+       0x00000000, 0x93430109, 0x3063007f, 0x00031140, 0x000318c0, 0x00431021,
+       0x24430088, 0xaf430818, 0x0000000d, 0x3c020800, 0x244241d0, 0x3c030800,
+       0x247741e0, 0x0a000bf4, 0xafa20020, 0x24420001, 0x0a000f4c, 0xac62002c,
+       0x8f840000, 0x8f850020, 0x24020800, 0xaf420178, 0x8f4209a4, 0x8c83017c,
+       0x00a21023, 0x00431023, 0x2c420002, 0x14400004, 0x00000000, 0x0000000d,
+       0x00000000, 0x24000349, 0x8f420104, 0x8f430988, 0x00431023, 0x58400005,
+       0x8f4209a0, 0x0000000d, 0x00000000, 0x2400034d, 0x8f4209a0, 0x3c100800,
+       0xae02414c, 0x8f4309a4, 0x2604414c, 0x2491ffd4, 0xae230030, 0x8f420104,
+       0xae250024, 0x00431023, 0xac82ffd4, 0x8fa30020, 0x8c620000, 0x0040f809,
+       0x0200b021, 0x00409021, 0x32440010, 0x32420002, 0x10400007, 0xafa40024,
+       0x8e22001c, 0x32500040, 0x2403ffbf, 0x00431024, 0x0a000f13, 0xae22001c,
+       0x32420020, 0x10400002, 0x3c020800, 0x245741b0, 0x32420001, 0x14400007,
+       0x00000000, 0x8f820008, 0xaf420080, 0x8ec3414c, 0xaf430e10, 0x8e220030,
+       0xaf420e18, 0x9343010b, 0x93420905, 0x30420008, 0x1040003c, 0x307400ff,
+       0x8f820000, 0x8c430074, 0x0460000a, 0x00000000, 0x3c026000, 0x24030100,
+       0xac43081c, 0x3c030001, 0xac43081c, 0x0000000d, 0x00000000, 0x24000384,
+       0x8f820000, 0x9044007b, 0x9343010a, 0x14830027, 0x32500040, 0x24072000,
+       0x3c090800, 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd, 0x8ec2414c,
+       0x26c4414c, 0x2484ffd4, 0xaf420144, 0x8c820030, 0x3c030100, 0xaf420148,
+       0x24020047, 0xaf43014c, 0x00001821, 0xa3420152, 0x3c021000, 0xa7430158,
+       0xaf470154, 0xaf420178, 0x8ec5414c, 0x8d230030, 0x8c860030, 0x24630001,
+       0xad230030, 0x93420109, 0x9343010a, 0xafa70014, 0xafa00018, 0x00021600,
+       0x00031c00, 0x00431025, 0x34424700, 0xafa20010, 0x8f440100, 0x0e000fe1,
+       0x3c070100, 0x3c030800, 0x24624120, 0x0a000d01, 0x8c43001c, 0x32820002,
+       0x10400047, 0x3c039000, 0x34630001, 0x8f820008, 0x32500040, 0x3c048000,
+       0x00431025, 0xaf420020, 0x8f420020, 0x00441024, 0x1440fffd, 0x00000000,
+       0x8f830000, 0x90620005, 0x3c058000, 0x34420008, 0xa0620005, 0x8f860000,
+       0x34a50001, 0x8f840008, 0x8cc20074, 0x3c038000, 0x00852025, 0x00431025,
+       0xacc20074, 0xaf440020, 0x90c3007b, 0x9342010a, 0x14620028, 0x3c040800,
+       0x24072000, 0x3c090800, 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd,
+       0x8ec2414c, 0x26c4414c, 0x2484ffd4, 0xaf420144, 0x8c820030, 0x3c030100,
+       0xaf420148, 0x24020046, 0xaf43014c, 0x00001821, 0xa3420152, 0x3c021000,
+       0xa7430158, 0xaf470154, 0xaf420178, 0x8ec5414c, 0x8d230030, 0x8c860030,
+       0x24630001, 0xad230030, 0x93420109, 0x9343010a, 0xafa70014, 0xafa00018,
+       0x00021600, 0x00031c00, 0x00431025, 0x34424600, 0xafa20010, 0x8f440100,
+       0x0e000fe1, 0x3c070100, 0x3c040800, 0x24824120, 0x0a000d01, 0x8c43001c,
+       0x93420108, 0x30420010, 0x50400050, 0x9343093f, 0x8f860000, 0x90c3007f,
+       0x90c2007e, 0x90c40080, 0x306800ff, 0x00021600, 0x00081c00, 0x00431025,
+       0x00042200, 0x90c3007a, 0x90c5000a, 0x00441025, 0x11050028, 0x00623825,
+       0xa0c8000a, 0x24086000, 0x3c090800, 0x3c038000, 0x8f420178, 0x00431024,
+       0x1440fffd, 0x8ec2414c, 0x26c4414c, 0x2484ffd4, 0xaf420144, 0x8c820030,
+       0x00001821, 0xaf420148, 0x24020052, 0xaf47014c, 0xa3420152, 0x3c021000,
+       0xa7430158, 0xaf480154, 0xaf420178, 0x8ec5414c, 0x8d230030, 0x8c860030,
+       0x24630001, 0xad230030, 0x93420109, 0x9343010a, 0xafa80014, 0xafa00018,
+       0x00021600, 0x00031c00, 0x00431025, 0x34425200, 0xafa20010, 0x0e000fe1,
+       0x8f440100, 0x0a000cfb, 0x00000000, 0x3c026000, 0x24030100, 0xac43081c,
+       0x3c030001, 0xac43081c, 0x0000000d, 0x00000000, 0x240003cd, 0x16800009,
+       0x3c040800, 0x3c030800, 0x24624120, 0x8c43001c, 0x32500040, 0x2404ffbf,
+       0x00641824, 0x0a000f13, 0xac43001c, 0x8c824120, 0x10400005, 0x3c030800,
+       0x8c620034, 0xac804120, 0x24420001, 0xac620034, 0x9343093f, 0x24020012,
+       0x1462000f, 0x329e0038, 0x17c0000c, 0x3c030800, 0x8f830000, 0x8c62004c,
+       0xac62005c, 0x3c020800, 0x24444120, 0x8c82001c, 0x32500040, 0x2403ffbf,
+       0x00431024, 0x0a000f13, 0xac82001c, 0xac604120, 0x97420908, 0x000211c0,
+       0xaf420024, 0x97420908, 0x3c030080, 0x34630003, 0x000211c0, 0xaf42080c,
+       0xaf43081c, 0x974209ec, 0x8f4309a4, 0xa7820028, 0x3c020800, 0x24444120,
+       0xac830028, 0x93420937, 0x93430934, 0x00021080, 0x00621821, 0xa4830014,
+       0x934209d8, 0x00621821, 0xa4830016, 0x934209d8, 0x93430934, 0x00809821,
+       0x00431021, 0x24420010, 0xa4820012, 0x0000a821, 0x24020006, 0x13c00003,
+       0xae62001c, 0x0a000d82, 0x24120008, 0x8f420958, 0x8f830020, 0x8f84002c,
+       0x00431023, 0x00832023, 0x04800003, 0xae620004, 0x04410003, 0x0082102b,
+       0x0a000d4e, 0xae600004, 0x54400001, 0xae640004, 0x8ee20000, 0x0040f809,
+       0x00000000, 0x00409021, 0x32420001, 0x5440001e, 0x8ee20004, 0x8e630008,
+       0x1060002b, 0x3c02c000, 0x00621025, 0xaf420e00, 0x8f420000, 0x30420008,
+       0x1040fffd, 0x00000000, 0x97420e08, 0xa7820010, 0x8f430e04, 0x8e620008,
+       0xaf830004, 0x8f840004, 0x0044102b, 0x1040000b, 0x24150001, 0x24020100,
+       0x3c016000, 0xac22081c, 0x3c020001, 0x3c016000, 0xac22081c, 0x0000000d,
+       0x00000000, 0x24000449, 0x24150001, 0x8ee20004, 0x0040f809, 0x00000000,
+       0x02429025, 0x32420002, 0x5040001d, 0x8f470940, 0x12a00006, 0x8ec2414c,
+       0x8f830000, 0xac6200a8, 0x8f840000, 0x8e620030, 0xac8200ac, 0x32420004,
+       0x50400013, 0x8f470940, 0x3c020800, 0x3283007d, 0x106000fe, 0x245741b0,
+       0x32820001, 0x50400006, 0x36520002, 0x8f830030, 0x8f420940, 0x106200f7,
+       0x00000000, 0x36520002, 0x24020008, 0xa660000c, 0xa662000e, 0xae600008,
+       0xa2600020, 0x8f470940, 0x3c030800, 0x24684120, 0x8d020028, 0x8d050008,
+       0x9504000c, 0x9506000a, 0x95030022, 0x00451021, 0x00862021, 0x00641821,
+       0xaf870030, 0xad020028, 0x32820030, 0x10400006, 0xa5030010, 0x91020020,
+       0x32910040, 0x34420004, 0x0a000dd4, 0xa1020020, 0x93420923, 0x30420040,
+       0x10400029, 0x32910040, 0x8f830000, 0x8f840020, 0x8c620084, 0x00441023,
+       0x0442000a, 0x3c039000, 0x95020010, 0x8c630084, 0x00821021, 0x00621823,
+       0x1c600004, 0x3c039000, 0x91020020, 0x34420001, 0xa1020020, 0x34630001,
+       0x8f820008, 0x32910040, 0x3c048000, 0x00431025, 0xaf420020, 0x8f420020,
+       0x00441024, 0x1440fffd, 0x00000000, 0x8f840000, 0x9083003f, 0x2402000a,
+       0x10620005, 0x2402000c, 0x9083003f, 0x24020008, 0x14620002, 0x24020014,
+       0xa082003f, 0x8f830008, 0x3c028000, 0x34420001, 0x00621825, 0xaf430020,
+       0x3c040800, 0x24904120, 0x9602000c, 0x96030016, 0x9604000e, 0x00431021,
+       0x00442021, 0x24840002, 0x3084ffff, 0x0e000a55, 0xa6020018, 0x8f850018,
+       0x00a01821, 0xa2030021, 0x8ee60008, 0x00402021, 0x24a50001, 0xaf850018,
+       0x00c0f809, 0x00000000, 0x00402021, 0x0e000b12, 0x02202821, 0x8ee3000c,
+       0x0060f809, 0x00402021, 0x96040018, 0x9602000e, 0x00822021, 0x24840002,
+       0x0e000a6b, 0x3084ffff, 0x3c030800, 0x8c624120, 0x8e030008, 0x3c040800,
+       0x00431023, 0x14400012, 0xac824120, 0x54600006, 0x8e02001c, 0x3243004a,
+       0x24020002, 0x14620005, 0x00000000, 0x8e02001c, 0x34420040, 0x0a000e0b,
+       0xae02001c, 0x52a00006, 0x36520002, 0x8e02002c, 0xaf420e10, 0x8e030030,
+       0xaf430e18, 0x36520002, 0x52a00008, 0x96670010, 0x8f830000, 0x8f420e10,
+       0xac6200a8, 0x8f840000, 0x8f420e18, 0xac8200ac, 0x96670010, 0x92680020,
+       0x24020040, 0xaf420814, 0x8f830020, 0x8f82001c, 0x00671821, 0x00621023,
+       0xaf830020, 0x58400005, 0x8f42095c, 0x8f820000, 0xaf83001c, 0xac430054,
+       0x8f42095c, 0x31030008, 0xaf82002c, 0x1060001a, 0x00000000, 0x8f840000,
+       0x90820120, 0x90830121, 0x304600ff, 0x00c31823, 0x30630007, 0x24020007,
+       0x1062000e, 0x00000000, 0x90820122, 0x304200fe, 0xa0820122, 0x8f850000,
+       0x00061880, 0x8f840020, 0x24a20100, 0x00431021, 0x24c30001, 0x30630007,
+       0xac440000, 0x0a000e40, 0xa0a30120, 0x90820122, 0x34420001, 0xa0820122,
+       0x14e00003, 0x31020001, 0x10400031, 0x32510002, 0x8f820000, 0x8c43000c,
+       0x30630001, 0x1060002c, 0x32510002, 0x3c029000, 0x8f830008, 0x34420001,
+       0x3c048000, 0x00621825, 0xaf430020, 0x8f420020, 0x00441024, 0x1440fffd,
+       0x00000000, 0x8f870000, 0x8ce2000c, 0x30420001, 0x10400018, 0x00000000,
+       0x94e2006a, 0x00022880, 0x50a00001, 0x24050001, 0x94e30068, 0x90e40081,
+       0x3c020800, 0x8c460024, 0x00652821, 0x00852804, 0x00c5102b, 0x54400001,
+       0x00a03021, 0x3c020800, 0x8c440028, 0x00c4182b, 0x54600001, 0x00c02021,
+       0x8f430074, 0x2402fffe, 0x00822824, 0x00a31821, 0xace3000c, 0x8f830008,
+       0x3c028000, 0x34420001, 0x00621825, 0xaf430020, 0x8f830020, 0x3c020800,
+       0x24504120, 0xae030024, 0x8ee20010, 0x0040f809, 0x00000000, 0x12a00005,
+       0x00000000, 0x8f420e10, 0xae02002c, 0x8f430e18, 0xae030030, 0x1220feba,
+       0x0000a821, 0x8f870024, 0x97860028, 0x8f830000, 0x8f820030, 0x8f840020,
+       0x8f85001c, 0x32500040, 0xa4e6002c, 0xac620044, 0x32420008, 0xac640050,
+       0xac650054, 0x1040007a, 0x32820020, 0x10400027, 0x32910010, 0x24072000,
+       0x3c090800, 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd, 0x8ec2414c,
+       0x26c4414c, 0x2484ffd4, 0xaf420144, 0x8c820030, 0x3c030400, 0xaf420148,
+       0x24020041, 0xaf43014c, 0x00001821, 0xa3420152, 0x3c021000, 0xa7430158,
+       0xaf470154, 0xaf420178, 0x8ec5414c, 0x8d230030, 0x8c860030, 0x24630001,
+       0xad230030, 0x93420109, 0x9343010a, 0xafa70014, 0xafa00018, 0x00021600,
+       0x00031c00, 0x00431025, 0x34424100, 0xafa20010, 0x8f440100, 0x0e000fe1,
+       0x3c070400, 0x12200028, 0x24072000, 0x3c090800, 0x3c038000, 0x8f420178,
+       0x00431024, 0x1440fffd, 0x8ec2414c, 0x26c4414c, 0x2484ffd4, 0xaf420144,
+       0x8c820030, 0x3c030300, 0xaf420148, 0x2402004e, 0xaf43014c, 0x00001821,
+       0xa3420152, 0x3c021000, 0xa7430158, 0xaf470154, 0xaf420178, 0x8ec5414c,
+       0x8d230030, 0x8c860030, 0x24630001, 0xad230030, 0x93420109, 0x9343010a,
+       0xafa70014, 0xafa00018, 0x00021600, 0x00031c00, 0x00431025, 0x34424e00,
+       0xafa20010, 0x8f440100, 0x0e000fe1, 0x3c070300, 0x0a000f0b, 0x8fa30024,
+       0x32820008, 0x10400026, 0x3c090800, 0x24072000, 0x3c038000, 0x8f420178,
+       0x00431024, 0x1440fffd, 0x8ec2414c, 0x26c4414c, 0x2484ffd4, 0xaf420144,
+       0x8c820030, 0x3c030200, 0xaf420148, 0x2402004b, 0xaf43014c, 0x00001821,
+       0xa3420152, 0x3c021000, 0xa7430158, 0xaf470154, 0xaf420178, 0x8ec5414c,
+       0x8d230030, 0x8c860030, 0x24630001, 0xad230030, 0x93420109, 0x9343010a,
+       0xafa70014, 0xafa00018, 0x00021600, 0x00031c00, 0x00431025, 0x34424b00,
+       0xafa20010, 0x8f440100, 0x0e000fe1, 0x3c070200, 0x8fa30024, 0x14600004,
+       0x8fa40020, 0x32420010, 0x10400004, 0x00000000, 0x8c820004, 0x0040f809,
+       0x00000000, 0x12000006, 0x8fa30020, 0x8c620008, 0x0040f809, 0x00000000,
+       0x0a000f4d, 0x8fbf004c, 0x3c030800, 0x8c62413c, 0x30420040, 0x1440002f,
+       0x8fbf004c, 0x24040040, 0x8f910020, 0x3c038000, 0x8f420178, 0x00431024,
+       0x1440fffd, 0x8ec2414c, 0x26d0414c, 0x2610ffd4, 0xaf420144, 0x8e020030,
+       0x00001821, 0xaf420148, 0x24020049, 0xaf51014c, 0xa3420152, 0x3c021000,
+       0xa7430158, 0xaf440154, 0xaf420178, 0x8ec5414c, 0x8e060030, 0x93420109,
+       0x9343010a, 0xafa40014, 0xafa00018, 0x00021600, 0x00031c00, 0x00431025,
+       0x34424900, 0xafa20010, 0x8f440100, 0x0e000fe1, 0x02203821, 0x8f830000,
+       0x8e020030, 0x8c64017c, 0x02221023, 0x00441023, 0x2c420002, 0x14400005,
+       0x8fbf004c, 0x0000000d, 0x00000000, 0x240000ca, 0x8fbf004c, 0x8fbe0048,
+       0x8fb70044, 0x8fb60040, 0x8fb5003c, 0x8fb40038, 0x8fb30034, 0x8fb20030,
+       0x8fb1002c, 0x8fb00028, 0x03e00008, 0x27bd0050, 0x03e00008, 0x00001021,
+       0x3c030800, 0x24654120, 0x8ca40004, 0x8c634120, 0x0064102b, 0x54400001,
+       0x00602021, 0x9743093c, 0x0083102b, 0x54400001, 0x00801821, 0x00001021,
+       0xaca30008, 0x03e00008, 0xa4a00022, 0x8f850004, 0x97840010, 0x3c030800,
+       0x24634120, 0x24020008, 0xa462000e, 0x8f820004, 0xa460000c, 0x000420c2,
+       0x30840008, 0x2c420001, 0x00021023, 0x30420006, 0xac650008, 0x03e00008,
+       0xa0640020, 0x3c020800, 0x24424120, 0x90450021, 0x94430018, 0x3c021100,
+       0xac800004, 0x00052c00, 0x24630002, 0x00621825, 0x00a32825, 0x24820008,
+       0x03e00008, 0xac850000, 0x0000000d, 0x00000000, 0x2400016f, 0x03e00008,
+       0x00000000, 0x0000000d, 0x00000000, 0x2400017b, 0x03e00008, 0x00000000,
+       0x03e00008, 0x00000000, 0x3c020800, 0x24424120, 0xac400008, 0xa4400022,
+       0x03e00008, 0x24020001, 0x3c020800, 0x24424120, 0x24030008, 0xac400008,
+       0xa440000c, 0xa443000e, 0xa0400020, 0x03e00008, 0x24020004, 0x03e00008,
+       0x00001021, 0x10c00007, 0x00000000, 0x8ca20000, 0x24c6ffff, 0x24a50004,
+       0xac820000, 0x14c0fffb, 0x24840004, 0x03e00008, 0x00000000, 0x0a000fb2,
+       0x00a01021, 0xac860000, 0x24840004, 0x00a01021, 0x1440fffc, 0x24a5ffff,
+       0x03e00008, 0x00000000, 0x3c0a0800, 0x8d490068, 0x3c050800, 0x24a51090,
+       0x00093140, 0x00c51021, 0xac440000, 0x8f440e04, 0x00a61021, 0xac440004,
+       0x97430e08, 0x97420e0c, 0x00a62021, 0x00031c00, 0x00431025, 0xac820008,
+       0x8f430e10, 0x00801021, 0xac43000c, 0x8f440e14, 0xac440010, 0x8f430e18,
+       0x3c0800ff, 0xac430014, 0x8f470e1c, 0x3508ffff, 0x25290001, 0xac470018,
+       0x3c070800, 0x8ce3006c, 0x9344010a, 0x3c026000, 0x24630001, 0xace3006c,
+       0x8c434448, 0x3129007f, 0x00a62821, 0xad490068, 0x00042600, 0x00681824,
+       0x00832025, 0x03e00008, 0xaca4001c, 0x8fac0010, 0x8fad0014, 0x8fae0018,
+       0x3c0b0800, 0x8d6a0060, 0x3c080800, 0x25080078, 0x000a4940, 0x01281021,
+       0x01091821, 0xac440000, 0x00601021, 0xac650004, 0xac460008, 0xac67000c,
+       0xac4c0010, 0xac6d0014, 0x3c036000, 0xac4e0018, 0x8c654448, 0x3c040800,
+       0x8c820064, 0x254a0001, 0x314a007f, 0x01094021, 0xad6a0060, 0x24420001,
+       0xac820064, 0x03e00008, 0xad05001c, 0x00000000 };
+
+static u32 bnx2_TXP_b06FwData[(0x0/4) + 1] = { 0x00000000 };
+static u32 bnx2_TXP_b06FwRodata[(0x0/4) + 1] = { 0x00000000 };
+static u32 bnx2_TXP_b06FwBss[(0x194/4) + 1] = { 0x00000000 };
+static u32 bnx2_TXP_b06FwSbss[(0x34/4) + 1] = { 0x00000000 };
+
diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c
new file mode 100644 (file)
index 0000000..67c8f3b
--- /dev/null
@@ -0,0 +1,735 @@
+/* 
+ *    Interfaces to retrieve and set PDC Stable options (firmware)
+ *
+ *    Copyright (C) 2005 Thibaut VARENE <varenet@parisc-linux.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
+ *
+ *
+ *    DEV NOTE: the PDC Procedures reference states that:
+ *    "A minimum of 96 bytes of Stable Storage is required. Providing more than
+ *    96 bytes of Stable Storage is optional [...]. Failure to provide the
+ *    optional locations from 96 to 192 results in the loss of certain
+ *    functionality during boot."
+ *
+ *    Since locations between 96 and 192 are the various paths, most (if not
+ *    all) PA-RISC machines should have them. Anyway, for safety reasons, the
+ *    following code can deal with only 96 bytes of Stable Storage, and all
+ *    sizes between 96 and 192 bytes (provided they are multiple of struct
+ *    device_path size, eg: 128, 160 and 192) to provide full information.
+ *    The code makes no use of data above 192 bytes. One last word: there's one
+ *    path we can always count on: the primary path.
+ */
+
+#undef PDCS_DEBUG
+#ifdef PDCS_DEBUG
+#define DPRINTK(fmt, args...)  printk(KERN_DEBUG fmt, ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>               /* for capable() */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+
+#include <asm/pdc.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+#include <asm/hardware.h>
+
+#define PDCS_VERSION   "0.09"
+
+#define PDCS_ADDR_PPRI 0x00
+#define PDCS_ADDR_OSID 0x40
+#define PDCS_ADDR_FSIZ 0x5C
+#define PDCS_ADDR_PCON 0x60
+#define PDCS_ADDR_PALT 0x80
+#define PDCS_ADDR_PKBD 0xA0
+
+MODULE_AUTHOR("Thibaut VARENE <varenet@parisc-linux.org>");
+MODULE_DESCRIPTION("sysfs interface to HP PDC Stable Storage data");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PDCS_VERSION);
+
+static unsigned long pdcs_size = 0;
+
+/* This struct defines what we need to deal with a parisc pdc path entry */
+struct pdcspath_entry {
+       short ready;                    /* entry record is valid if != 0 */
+       unsigned long addr;             /* entry address in stable storage */
+       char *name;                     /* entry name */
+       struct device_path devpath;     /* device path in parisc representation */
+       struct device *dev;             /* corresponding device */
+       struct kobject kobj;
+};
+
+struct pdcspath_attribute {
+       struct attribute attr;
+       ssize_t (*show)(struct pdcspath_entry *entry, char *buf);
+       ssize_t (*store)(struct pdcspath_entry *entry, const char *buf, size_t count);
+};
+
+#define PDCSPATH_ENTRY(_addr, _name) \
+struct pdcspath_entry pdcspath_entry_##_name = { \
+       .ready = 0, \
+       .addr = _addr, \
+       .name = __stringify(_name), \
+};
+
+#define PDCS_ATTR(_name, _mode, _show, _store) \
+struct subsys_attribute pdcs_attr_##_name = { \
+       .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \
+       .show = _show, \
+       .store = _store, \
+};
+
+#define PATHS_ATTR(_name, _mode, _show, _store) \
+struct pdcspath_attribute paths_attr_##_name = { \
+       .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \
+       .show = _show, \
+       .store = _store, \
+};
+
+#define to_pdcspath_attribute(_attr) container_of(_attr, struct pdcspath_attribute, attr)
+#define to_pdcspath_entry(obj)  container_of(obj, struct pdcspath_entry, kobj)
+
+/**
+ * pdcspath_fetch - This function populates the path entry structs.
+ * @entry: A pointer to an allocated pdcspath_entry.
+ * 
+ * The general idea is that you don't read from the Stable Storage every time
+ * you access the files provided by the facilites. We store a copy of the
+ * content of the stable storage WRT various paths in these structs. We read
+ * these structs when reading the files, and we will write to these structs when
+ * writing to the files, and only then write them back to the Stable Storage.
+ */
+static int
+pdcspath_fetch(struct pdcspath_entry *entry)
+{
+       struct device_path *devpath;
+
+       if (!entry)
+               return -EINVAL;
+
+       devpath = &entry->devpath;
+       
+       DPRINTK("%s: fetch: 0x%p, 0x%p, addr: 0x%lx\n", __func__,
+                       entry, devpath, entry->addr);
+
+       /* addr, devpath and count must be word aligned */
+       if (pdc_stable_read(entry->addr, devpath, sizeof(*devpath)) != PDC_OK)
+               return -EIO;
+               
+       /* Find the matching device.
+          NOTE: hardware_path overlays with device_path, so the nice cast can
+          be used */
+       entry->dev = hwpath_to_device((struct hardware_path *)devpath);
+
+       entry->ready = 1;
+       
+       DPRINTK("%s: device: 0x%p\n", __func__, entry->dev);
+       
+       return 0;
+}
+
+/**
+ * pdcspath_store - This function writes a path to stable storage.
+ * @entry: A pointer to an allocated pdcspath_entry.
+ * 
+ * It can be used in two ways: either by passing it a preset devpath struct
+ * containing an already computed hardware path, or by passing it a device
+ * pointer, from which it'll find out the corresponding hardware path.
+ * For now we do not handle the case where there's an error in writing to the
+ * Stable Storage area, so you'd better not mess up the data :P
+ */
+static int
+pdcspath_store(struct pdcspath_entry *entry)
+{
+       struct device_path *devpath;
+
+       if (!entry)
+               return -EINVAL;
+
+       devpath = &entry->devpath;
+       
+       /* We expect the caller to set the ready flag to 0 if the hardware
+          path struct provided is invalid, so that we know we have to fill it.
+          First case, we don't have a preset hwpath... */
+       if (!entry->ready) {
+               /* ...but we have a device, map it */
+               if (entry->dev)
+                       device_to_hwpath(entry->dev, (struct hardware_path *)devpath);
+               else
+                       return -EINVAL;
+       }
+       /* else, we expect the provided hwpath to be valid. */
+       
+       DPRINTK("%s: store: 0x%p, 0x%p, addr: 0x%lx\n", __func__,
+                       entry, devpath, entry->addr);
+
+       /* addr, devpath and count must be word aligned */
+       if (pdc_stable_write(entry->addr, devpath, sizeof(*devpath)) != PDC_OK) {
+               printk(KERN_ERR "%s: an error occured when writing to PDC.\n"
+                               "It is likely that the Stable Storage data has been corrupted.\n"
+                               "Please check it carefully upon next reboot.\n", __func__);
+               return -EIO;
+       }
+               
+       entry->ready = 1;
+       
+       DPRINTK("%s: device: 0x%p\n", __func__, entry->dev);
+       
+       return 0;
+}
+
+/**
+ * pdcspath_hwpath_read - This function handles hardware path pretty printing.
+ * @entry: An allocated and populated pdscpath_entry struct.
+ * @buf: The output buffer to write to.
+ * 
+ * We will call this function to format the output of the hwpath attribute file.
+ */
+static ssize_t
+pdcspath_hwpath_read(struct pdcspath_entry *entry, char *buf)
+{
+       char *out = buf;
+       struct device_path *devpath;
+       unsigned short i;
+
+       if (!entry || !buf)
+               return -EINVAL;
+
+       devpath = &entry->devpath;
+
+       if (!entry->ready)
+               return -ENODATA;
+       
+       for (i = 0; i < 6; i++) {
+               if (devpath->bc[i] >= 128)
+                       continue;
+               out += sprintf(out, "%u/", (unsigned char)devpath->bc[i]);
+       }
+       out += sprintf(out, "%u\n", (unsigned char)devpath->mod);
+       
+       return out - buf;
+}
+
+/**
+ * pdcspath_hwpath_write - This function handles hardware path modifying.
+ * @entry: An allocated and populated pdscpath_entry struct.
+ * @buf: The input buffer to read from.
+ * @count: The number of bytes to be read.
+ * 
+ * We will call this function to change the current hardware path.
+ * Hardware paths are to be given '/'-delimited, without brackets.
+ * We take care to make sure that the provided path actually maps to an existing
+ * device, BUT nothing would prevent some foolish user to set the path to some
+ * PCI bridge or even a CPU...
+ * A better work around would be to make sure we are at the end of a device tree
+ * for instance, but it would be IMHO beyond the simple scope of that driver.
+ * The aim is to provide a facility. Data correctness is left to userland.
+ */
+static ssize_t
+pdcspath_hwpath_write(struct pdcspath_entry *entry, const char *buf, size_t count)
+{
+       struct hardware_path hwpath;
+       unsigned short i;
+       char in[count+1], *temp;
+       struct device *dev;
+
+       if (!entry || !buf || !count)
+               return -EINVAL;
+
+       /* We'll use a local copy of buf */
+       memset(in, 0, count+1);
+       strncpy(in, buf, count);
+       
+       /* Let's clean up the target. 0xff is a blank pattern */
+       memset(&hwpath, 0xff, sizeof(hwpath));
+       
+       /* First, pick the mod field (the last one of the input string) */
+       if (!(temp = strrchr(in, '/')))
+               return -EINVAL;
+                       
+       hwpath.mod = simple_strtoul(temp+1, NULL, 10);
+       in[temp-in] = '\0';     /* truncate the remaining string. just precaution */
+       DPRINTK("%s: mod: %d\n", __func__, hwpath.mod);
+       
+       /* Then, loop for each delimiter, making sure we don't have too many.
+          we write the bc fields in a down-top way. No matter what, we stop
+          before writing the last field. If there are too many fields anyway,
+          then the user is a moron and it'll be caught up later when we'll
+          check the consistency of the given hwpath. */
+       for (i=5; ((temp = strrchr(in, '/'))) && (temp-in > 0) && (likely(i)); i--) {
+               hwpath.bc[i] = simple_strtoul(temp+1, NULL, 10);
+               in[temp-in] = '\0';
+               DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.bc[i]);
+       }
+       
+       /* Store the final field */             
+       hwpath.bc[i] = simple_strtoul(in, NULL, 10);
+       DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.bc[i]);
+       
+       /* Now we check that the user isn't trying to lure us */
+       if (!(dev = hwpath_to_device((struct hardware_path *)&hwpath))) {
+               printk(KERN_WARNING "%s: attempt to set invalid \"%s\" "
+                       "hardware path: %s\n", __func__, entry->name, buf);
+               return -EINVAL;
+       }
+       
+       /* So far so good, let's get in deep */
+       entry->ready = 0;
+       entry->dev = dev;
+       
+       /* Now, dive in. Write back to the hardware */
+       WARN_ON(pdcspath_store(entry)); /* this warn should *NEVER* happen */
+       
+       /* Update the symlink to the real device */
+       sysfs_remove_link(&entry->kobj, "device");
+       sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");
+       
+       printk(KERN_INFO "PDC Stable Storage: changed \"%s\" path to \"%s\"\n",
+               entry->name, buf);
+       
+       return count;
+}
+
+/**
+ * pdcspath_layer_read - Extended layer (eg. SCSI ids) pretty printing.
+ * @entry: An allocated and populated pdscpath_entry struct.
+ * @buf: The output buffer to write to.
+ * 
+ * We will call this function to format the output of the layer attribute file.
+ */
+static ssize_t
+pdcspath_layer_read(struct pdcspath_entry *entry, char *buf)
+{
+       char *out = buf;
+       struct device_path *devpath;
+       unsigned short i;
+
+       if (!entry || !buf)
+               return -EINVAL;
+       
+       devpath = &entry->devpath;
+
+       if (!entry->ready)
+               return -ENODATA;
+       
+       for (i = 0; devpath->layers[i] && (likely(i < 6)); i++)
+               out += sprintf(out, "%u ", devpath->layers[i]);
+
+       out += sprintf(out, "\n");
+       
+       return out - buf;
+}
+
+/**
+ * pdcspath_layer_write - This function handles extended layer modifying.
+ * @entry: An allocated and populated pdscpath_entry struct.
+ * @buf: The input buffer to read from.
+ * @count: The number of bytes to be read.
+ * 
+ * We will call this function to change the current layer value.
+ * Layers are to be given '.'-delimited, without brackets.
+ * XXX beware we are far less checky WRT input data provided than for hwpath.
+ * Potential harm can be done, since there's no way to check the validity of
+ * the layer fields.
+ */
+static ssize_t
+pdcspath_layer_write(struct pdcspath_entry *entry, const char *buf, size_t count)
+{
+       unsigned int layers[6]; /* device-specific info (ctlr#, unit#, ...) */
+       unsigned short i;
+       char in[count+1], *temp;
+
+       if (!entry || !buf || !count)
+               return -EINVAL;
+
+       /* We'll use a local copy of buf */
+       memset(in, 0, count+1);
+       strncpy(in, buf, count);
+       
+       /* Let's clean up the target. 0 is a blank pattern */
+       memset(&layers, 0, sizeof(layers));
+       
+       /* First, pick the first layer */
+       if (unlikely(!isdigit(*in)))
+               return -EINVAL;
+       layers[0] = simple_strtoul(in, NULL, 10);
+       DPRINTK("%s: layer[0]: %d\n", __func__, layers[0]);
+       
+       temp = in;
+       for (i=1; ((temp = strchr(temp, '.'))) && (likely(i<6)); i++) {
+               if (unlikely(!isdigit(*(++temp))))
+                       return -EINVAL;
+               layers[i] = simple_strtoul(temp, NULL, 10);
+               DPRINTK("%s: layer[%d]: %d\n", __func__, i, layers[i]);
+       }
+               
+       /* So far so good, let's get in deep */
+       
+       /* First, overwrite the current layers with the new ones, not touching
+          the hardware path. */
+       memcpy(&entry->devpath.layers, &layers, sizeof(layers));
+       
+       /* Now, dive in. Write back to the hardware */
+       WARN_ON(pdcspath_store(entry)); /* this warn should *NEVER* happen */
+       
+       printk(KERN_INFO "PDC Stable Storage: changed \"%s\" layers to \"%s\"\n",
+               entry->name, buf);
+       
+       return count;
+}
+
+/**
+ * pdcspath_attr_show - Generic read function call wrapper.
+ * @kobj: The kobject to get info from.
+ * @attr: The attribute looked upon.
+ * @buf: The output buffer.
+ */
+static ssize_t
+pdcspath_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+       struct pdcspath_entry *entry = to_pdcspath_entry(kobj);
+       struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr);
+       ssize_t ret = 0;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EACCES;
+
+       if (pdcs_attr->show)
+               ret = pdcs_attr->show(entry, buf);
+
+       return ret;
+}
+
+/**
+ * pdcspath_attr_store - Generic write function call wrapper.
+ * @kobj: The kobject to write info to.
+ * @attr: The attribute to be modified.
+ * @buf: The input buffer.
+ * @count: The size of the buffer.
+ */
+static ssize_t
+pdcspath_attr_store(struct kobject *kobj, struct attribute *attr,
+                       const char *buf, size_t count)
+{
+       struct pdcspath_entry *entry = to_pdcspath_entry(kobj);
+       struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr);
+       ssize_t ret = 0;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EACCES;
+
+       if (pdcs_attr->store)
+               ret = pdcs_attr->store(entry, buf, count);
+
+       return ret;
+}
+
+static struct sysfs_ops pdcspath_attr_ops = {
+       .show = pdcspath_attr_show,
+       .store = pdcspath_attr_store,
+};
+
+/* These are the two attributes of any PDC path. */
+static PATHS_ATTR(hwpath, 0600, pdcspath_hwpath_read, pdcspath_hwpath_write);
+static PATHS_ATTR(layer, 0600, pdcspath_layer_read, pdcspath_layer_write);
+
+static struct attribute *paths_subsys_attrs[] = {
+       &paths_attr_hwpath.attr,
+       &paths_attr_layer.attr,
+       NULL,
+};
+
+/* Specific kobject type for our PDC paths */
+static struct kobj_type ktype_pdcspath = {
+       .sysfs_ops = &pdcspath_attr_ops,
+       .default_attrs = paths_subsys_attrs,
+};
+
+/* We hard define the 4 types of path we expect to find */
+static PDCSPATH_ENTRY(PDCS_ADDR_PPRI, primary);
+static PDCSPATH_ENTRY(PDCS_ADDR_PCON, console);
+static PDCSPATH_ENTRY(PDCS_ADDR_PALT, alternative);
+static PDCSPATH_ENTRY(PDCS_ADDR_PKBD, keyboard);
+
+/* An array containing all PDC paths we will deal with */
+static struct pdcspath_entry *pdcspath_entries[] = {
+       &pdcspath_entry_primary,
+       &pdcspath_entry_alternative,
+       &pdcspath_entry_console,
+       &pdcspath_entry_keyboard,
+       NULL,
+};
+
+/**
+ * pdcs_info_read - Pretty printing of the remaining useful data.
+ * @entry: An allocated and populated subsytem struct. We don't use it tho.
+ * @buf: The output buffer to write to.
+ * 
+ * We will call this function to format the output of the 'info' attribute file.
+ * Please refer to PDC Procedures documentation, section PDC_STABLE to get a
+ * better insight of what we're doing here.
+ */
+static ssize_t
+pdcs_info_read(struct subsystem *entry, char *buf)
+{
+       char *out = buf;
+       __u32 result;
+       struct device_path devpath;
+       char *tmpstr = NULL;
+       
+       if (!entry || !buf)
+               return -EINVAL;
+               
+       /* show the size of the stable storage */
+       out += sprintf(out, "Stable Storage size: %ld bytes\n", pdcs_size);
+
+       /* deal with flags */
+       if (pdc_stable_read(PDCS_ADDR_PPRI, &devpath, sizeof(devpath)) != PDC_OK)
+               return -EIO;
+       
+       out += sprintf(out, "Autoboot: %s\n", (devpath.flags & PF_AUTOBOOT) ? "On" : "Off");
+       out += sprintf(out, "Autosearch: %s\n", (devpath.flags & PF_AUTOSEARCH) ? "On" : "Off");
+       out += sprintf(out, "Timer: %u s\n", (devpath.flags & PF_TIMER) ? (1 << (devpath.flags & PF_TIMER)) : 0);
+
+       /* get OSID */
+       if (pdc_stable_read(PDCS_ADDR_OSID, &result, sizeof(result)) != PDC_OK)
+               return -EIO;
+
+       /* the actual result is 16 bits away */
+       switch (result >> 16) {
+               case 0x0000:    tmpstr = "No OS-dependent data"; break;
+               case 0x0001:    tmpstr = "HP-UX dependent data"; break;
+               case 0x0002:    tmpstr = "MPE-iX dependent data"; break;
+               case 0x0003:    tmpstr = "OSF dependent data"; break;
+               case 0x0004:    tmpstr = "HP-RT dependent data"; break;
+               case 0x0005:    tmpstr = "Novell Netware dependent data"; break;
+               default:        tmpstr = "Unknown"; break;
+       }
+       out += sprintf(out, "OS ID: %s (0x%.4x)\n", tmpstr, (result >> 16));
+
+       /* get fast-size */
+       if (pdc_stable_read(PDCS_ADDR_FSIZ, &result, sizeof(result)) != PDC_OK)
+               return -EIO;
+
+       out += sprintf(out, "Memory tested: ");
+       if ((result & 0x0F) < 0x0E)
+               out += sprintf(out, "%.3f MB", 0.256*(1<<(result & 0x0F)));
+       else
+               out += sprintf(out, "All");
+       out += sprintf(out, "\n");
+       
+       return out - buf;
+}
+
+/**
+ * pdcs_info_write - This function handles boot flag modifying.
+ * @entry: An allocated and populated subsytem struct. We don't use it tho.
+ * @buf: The input buffer to read from.
+ * @count: The number of bytes to be read.
+ * 
+ * We will call this function to change the current boot flags.
+ * We expect a precise syntax:
+ *     \"n n\" (n == 0 or 1) to toggle respectively AutoBoot and AutoSearch
+ *
+ * As of now there is no incentive on my side to provide more "knobs" to that
+ * interface, since modifying the rest of the data is pretty meaningless when
+ * the machine is running and for the expected use of that facility, such as
+ * PALO setting up the boot disk when installing a Linux distribution...
+ */
+static ssize_t
+pdcs_info_write(struct subsystem *entry, const char *buf, size_t count)
+{
+       struct pdcspath_entry *pathentry;
+       unsigned char flags;
+       char in[count+1], *temp;
+       char c;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EACCES;
+
+       if (!entry || !buf || !count)
+               return -EINVAL;
+
+       /* We'll use a local copy of buf */
+       memset(in, 0, count+1);
+       strncpy(in, buf, count);
+
+       /* Current flags are stored in primary boot path entry */
+       pathentry = &pdcspath_entry_primary;
+       
+       /* Be nice to the existing flag record */
+       flags = pathentry->devpath.flags;
+       
+       DPRINTK("%s: flags before: 0x%X\n", __func__, flags);
+                       
+       temp = in;
+       
+       while (*temp && isspace(*temp))
+               temp++;
+       
+       c = *temp++ - '0';
+       if ((c != 0) && (c != 1))
+               goto parse_error;
+       if (c == 0)
+               flags &= ~PF_AUTOBOOT;
+       else
+               flags |= PF_AUTOBOOT;
+       
+       if (*temp++ != ' ')
+               goto parse_error;
+       
+       c = *temp++ - '0';
+       if ((c != 0) && (c != 1))
+               goto parse_error;
+       if (c == 0)
+               flags &= ~PF_AUTOSEARCH;
+       else
+               flags |= PF_AUTOSEARCH;
+       
+       DPRINTK("%s: flags after: 0x%X\n", __func__, flags);
+               
+       /* So far so good, let's get in deep */
+       
+       /* Change the path entry flags first */
+       pathentry->devpath.flags = flags;
+               
+       /* Now, dive in. Write back to the hardware */
+       WARN_ON(pdcspath_store(pathentry));     /* this warn should *NEVER* happen */
+       
+       printk(KERN_INFO "PDC Stable Storage: changed flags to \"%s\"\n", buf);
+       
+       return count;
+
+parse_error:
+       printk(KERN_WARNING "%s: Parse error: expect \"n n\" (n == 0 or 1) for AB and AS\n", __func__);
+       return -EINVAL;
+}
+
+/* The last attribute (the 'root' one actually) with all remaining data. */
+static PDCS_ATTR(info, 0600, pdcs_info_read, pdcs_info_write);
+
+static struct subsys_attribute *pdcs_subsys_attrs[] = {
+       &pdcs_attr_info,
+       NULL,   /* maybe more in the future? */
+};
+
+static decl_subsys(paths, &ktype_pdcspath, NULL);
+static decl_subsys(pdc, NULL, NULL);
+
+/**
+ * pdcs_register_pathentries - Prepares path entries kobjects for sysfs usage.
+ * 
+ * It creates kobjects corresponding to each path entry with nice sysfs
+ * links to the real device. This is where the magic takes place: when
+ * registering the subsystem attributes during module init, each kobject hereby
+ * created will show in the sysfs tree as a folder containing files as defined
+ * by path_subsys_attr[].
+ */
+static inline int __init
+pdcs_register_pathentries(void)
+{
+       unsigned short i;
+       struct pdcspath_entry *entry;
+       
+       for (i = 0; (entry = pdcspath_entries[i]); i++) {
+               if (pdcspath_fetch(entry) < 0)
+                       continue;
+
+               kobject_set_name(&entry->kobj, "%s", entry->name);
+               kobj_set_kset_s(entry, paths_subsys);
+               kobject_register(&entry->kobj);
+
+               if (!entry->dev)
+                       continue;
+
+               /* Add a nice symlink to the real device */
+               sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");
+       }
+       
+       return 0;
+}
+
+/**
+ * pdcs_unregister_pathentries - Routine called when unregistering the module.
+ */
+static inline void __exit
+pdcs_unregister_pathentries(void)
+{
+       unsigned short i;
+       struct pdcspath_entry *entry;
+       
+       for (i = 0; (entry = pdcspath_entries[i]); i++)
+               if (entry->ready)
+                       kobject_unregister(&entry->kobj);       
+}
+
+/*
+ * For now we register the pdc subsystem with the firmware subsystem
+ * and the paths subsystem with the pdc subsystem
+ */
+static int __init
+pdc_stable_init(void)
+{
+       struct subsys_attribute *attr;
+       int i, rc = 0, error = 0;
+
+       /* find the size of the stable storage */
+       if (pdc_stable_get_size(&pdcs_size) != PDC_OK) 
+               return -ENODEV;
+
+       printk(KERN_INFO "PDC Stable Storage facility v%s\n", PDCS_VERSION);
+
+       /* For now we'll register the pdc subsys within this driver */
+       if ((rc = firmware_register(&pdc_subsys)))
+               return rc;
+
+       /* Don't forget the info entry */
+       for (i = 0; (attr = pdcs_subsys_attrs[i]) && !error; i++)
+               if (attr->show)
+                       error = subsys_create_file(&pdc_subsys, attr);
+       
+       /* register the paths subsys as a subsystem of pdc subsys */
+       kset_set_kset_s(&paths_subsys, pdc_subsys);
+       subsystem_register(&paths_subsys);
+
+       /* now we create all "files" for the paths subsys */
+       pdcs_register_pathentries();
+       
+       return 0;
+}
+
+static void __exit
+pdc_stable_exit(void)
+{
+       pdcs_unregister_pathentries();
+       subsystem_unregister(&paths_subsys);
+
+       firmware_unregister(&pdc_subsys);
+}
+
+
+module_init(pdc_stable_init);
+module_exit(pdc_stable_exit);
diff --git a/drivers/parport/parport_gsc.h b/drivers/parport/parport_gsc.h
new file mode 100644 (file)
index 0000000..662f6c1
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ *     Low-level parallel-support for PC-style hardware integrated in the
+ *     LASI-Controller (on GSC-Bus) for HP-PARISC Workstations
+ *
+ *     (C) 1999-2001 by Helge Deller <deller@gmx.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
+ *
+ * based on parport_pc.c by
+ *         Grant Guenther <grant@torque.net>
+ *         Phil Blundell <Philip.Blundell@pobox.com>
+ *          Tim Waugh <tim@cyberelk.demon.co.uk>
+ *         Jose Renau <renau@acm.org>
+ *          David Campbell <campbell@torque.net>
+ *          Andrea Arcangeli
+ */
+
+#ifndef        __DRIVERS_PARPORT_PARPORT_GSC_H
+#define        __DRIVERS_PARPORT_PARPORT_GSC_H
+
+#include <asm/io.h>
+#include <linux/delay.h>
+
+#undef DEBUG_PARPORT   /* undefine for production */
+#define DELAY_TIME     0
+
+#if DELAY_TIME == 0
+#define parport_readb  gsc_readb
+#define parport_writeb gsc_writeb
+#else
+static __inline__ unsigned char parport_readb( unsigned long port )
+{
+    udelay(DELAY_TIME);
+    return gsc_readb(port);
+}
+
+static __inline__ void parport_writeb( unsigned char value, unsigned long port )
+{
+    gsc_writeb(value,port);
+    udelay(DELAY_TIME);
+}
+#endif
+
+/* --- register definitions ------------------------------- */
+
+#define EPPDATA(p)  ((p)->base    + 0x4)
+#define EPPADDR(p)  ((p)->base    + 0x3)
+#define CONTROL(p)  ((p)->base    + 0x2)
+#define STATUS(p)   ((p)->base    + 0x1)
+#define DATA(p)     ((p)->base    + 0x0)
+
+struct parport_gsc_private {
+       /* Contents of CTR. */
+       unsigned char ctr;
+
+       /* Bitmask of writable CTR bits. */
+       unsigned char ctr_writable;
+
+       /* Number of bytes per portword. */
+       int pword;
+
+       /* Not used yet. */
+       int readIntrThreshold;
+       int writeIntrThreshold;
+
+       /* buffer suitable for DMA, if DMA enabled */
+       char *dma_buf;
+       dma_addr_t dma_handle;
+       struct pci_dev *dev;
+};
+
+static inline void parport_gsc_write_data(struct parport *p, unsigned char d)
+{
+#ifdef DEBUG_PARPORT
+       printk (KERN_DEBUG "parport_gsc_write_data(%p,0x%02x)\n", p, d);
+#endif
+       parport_writeb(d, DATA(p));
+}
+
+static inline unsigned char parport_gsc_read_data(struct parport *p)
+{
+       unsigned char val = parport_readb (DATA (p));
+#ifdef DEBUG_PARPORT
+       printk (KERN_DEBUG "parport_gsc_read_data(%p) = 0x%02x\n",
+               p, val);
+#endif
+       return val;
+}
+
+/* __parport_gsc_frob_control differs from parport_gsc_frob_control in that
+ * it doesn't do any extra masking. */
+static inline unsigned char __parport_gsc_frob_control(struct parport *p,
+                                                       unsigned char mask,
+                                                       unsigned char val)
+{
+       struct parport_gsc_private *priv = p->physport->private_data;
+       unsigned char ctr = priv->ctr;
+#ifdef DEBUG_PARPORT
+       printk (KERN_DEBUG
+               "__parport_gsc_frob_control(%02x,%02x): %02x -> %02x\n",
+               mask, val, ctr, ((ctr & ~mask) ^ val) & priv->ctr_writable);
+#endif
+       ctr = (ctr & ~mask) ^ val;
+       ctr &= priv->ctr_writable; /* only write writable bits. */
+       parport_writeb (ctr, CONTROL (p));
+       priv->ctr = ctr;        /* Update soft copy */
+       return ctr;
+}
+
+static inline void parport_gsc_data_reverse(struct parport *p)
+{
+       __parport_gsc_frob_control (p, 0x20, 0x20);
+}
+
+static inline void parport_gsc_data_forward(struct parport *p)
+{
+       __parport_gsc_frob_control (p, 0x20, 0x00);
+}
+
+static inline void parport_gsc_write_control(struct parport *p,
+                                                unsigned char d)
+{
+       const unsigned char wm = (PARPORT_CONTROL_STROBE |
+                                 PARPORT_CONTROL_AUTOFD |
+                                 PARPORT_CONTROL_INIT |
+                                 PARPORT_CONTROL_SELECT);
+
+       /* Take this out when drivers have adapted to newer interface. */
+       if (d & 0x20) {
+               printk (KERN_DEBUG "%s (%s): use data_reverse for this!\n",
+                       p->name, p->cad->name);
+               parport_gsc_data_reverse (p);
+       }
+
+       __parport_gsc_frob_control (p, wm, d & wm);
+}
+
+static inline unsigned char parport_gsc_read_control(struct parport *p)
+{
+       const unsigned char rm = (PARPORT_CONTROL_STROBE |
+                                 PARPORT_CONTROL_AUTOFD |
+                                 PARPORT_CONTROL_INIT |
+                                 PARPORT_CONTROL_SELECT);
+       const struct parport_gsc_private *priv = p->physport->private_data;
+       return priv->ctr & rm; /* Use soft copy */
+}
+
+static inline unsigned char parport_gsc_frob_control(struct parport *p,
+                                                       unsigned char mask,
+                                                       unsigned char val)
+{
+       const unsigned char wm = (PARPORT_CONTROL_STROBE |
+                                 PARPORT_CONTROL_AUTOFD |
+                                 PARPORT_CONTROL_INIT |
+                                 PARPORT_CONTROL_SELECT);
+
+       /* Take this out when drivers have adapted to newer interface. */
+       if (mask & 0x20) {
+               printk (KERN_DEBUG "%s (%s): use data_%s for this!\n",
+                       p->name, p->cad->name,
+                       (val & 0x20) ? "reverse" : "forward");
+               if (val & 0x20)
+                       parport_gsc_data_reverse (p);
+               else
+                       parport_gsc_data_forward (p);
+       }
+
+       /* Restrict mask and val to control lines. */
+       mask &= wm;
+       val &= wm;
+
+       return __parport_gsc_frob_control (p, mask, val);
+}
+
+static inline unsigned char parport_gsc_read_status(struct parport *p)
+{
+       return parport_readb (STATUS(p));
+}
+
+static inline void parport_gsc_disable_irq(struct parport *p)
+{
+       __parport_gsc_frob_control (p, 0x10, 0x00);
+}
+
+static inline void parport_gsc_enable_irq(struct parport *p)
+{
+       __parport_gsc_frob_control (p, 0x10, 0x10);
+}
+
+extern void parport_gsc_release_resources(struct parport *p);
+
+extern int parport_gsc_claim_resources(struct parport *p);
+
+extern void parport_gsc_init_state(struct pardevice *, struct parport_state *s);
+
+extern void parport_gsc_save_state(struct parport *p, struct parport_state *s);
+
+extern void parport_gsc_restore_state(struct parport *p, struct parport_state *s);
+
+extern void parport_gsc_inc_use_count(void);
+
+extern void parport_gsc_dec_use_count(void);
+
+extern struct parport *parport_gsc_probe_port(unsigned long base,
+                                               unsigned long base_hi,
+                                               int irq, int dma,
+                                               struct pci_dev *dev);
+
+#endif /* __DRIVERS_PARPORT_PARPORT_GSC_H */
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
new file mode 100644 (file)
index 0000000..06804d3
--- /dev/null
@@ -0,0 +1,4447 @@
+/*
+ *  drivers/s390/net/claw.c
+ *    ESCON CLAW network driver
+ *
+ *    $Revision: 1.35 $ $Date: 2005/03/24 12:25:38 $
+ *
+ *  Linux fo zSeries version
+ *    Copyright (C) 2002,2005 IBM Corporation
+ *  Author(s) Original code written by:
+ *              Kazuo Iimura (iimura@jp.ibm.com)
+ *           Rewritten by
+ *              Andy Richter (richtera@us.ibm.com)
+ *              Marc Price (mwprice@us.ibm.com)
+ *
+ *    sysfs parms:
+ *   group x.x.rrrr,x.x.wwww
+ *   read_buffer nnnnnnn
+ *   write_buffer nnnnnn
+ *   host_name  aaaaaaaa
+ *   adapter_name aaaaaaaa
+ *   api_type    aaaaaaaa
+ *
+ *  eg.
+ *   group  0.0.0200 0.0.0201
+ *   read_buffer 25
+ *   write_buffer 20
+ *   host_name LINUX390
+ *   adapter_name RS6K
+ *   api_type     TCPIP
+ *
+ *  where
+ *
+ *   The device id is decided by the order entries
+ *   are added to the group the first is claw0 the second claw1
+ *   up to CLAW_MAX_DEV
+ *
+ *   rrrr     -        the first of 2 consecutive device addresses used for the
+ *             CLAW protocol.
+ *             The specified address is always used as the input (Read)
+ *             channel and the next address is used as the output channel.
+ *
+ *   wwww     -        the second of 2 consecutive device addresses used for
+ *             the CLAW protocol.
+ *              The specified address is always used as the output
+ *             channel and the previous address is used as the input channel.
+ *
+ *   read_buffer       -       specifies number of input buffers to allocate.
+ *   write_buffer       -       specifies number of output buffers to allocate.
+ *   host_name          -       host name
+ *   adaptor_name       -       adaptor name
+ *   api_type           -       API type TCPIP or API will be sent and expected
+ *                             as ws_name
+ *
+ *   Note the following requirements:
+ *   1)  host_name must match the configured adapter_name on the remote side
+ *   2)  adaptor_name must match the configured host name on the remote side
+ *
+ *  Change History
+ *    1.00  Initial release shipped
+ *    1.10  Changes for Buffer allocation
+ *    1.15  Changed for 2.6 Kernel  No longer compiles on 2.4 or lower
+ *    1.25  Added Packing support
+ */
+#include <asm/bitops.h>
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+#include <asm/debug.h>
+#include <asm/idals.h>
+#include <asm/io.h>
+
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/tcp.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/version.h>
+
+#include "cu3088.h"
+#include "claw.h"
+
+MODULE_AUTHOR("Andy Richter <richtera@us.ibm.com>");
+MODULE_DESCRIPTION("Linux for zSeries CLAW Driver\n" \
+                       "Copyright 2000,2005 IBM Corporation\n");
+MODULE_LICENSE("GPL");
+
+/* Debugging is based on DEBUGMSG, IOTRACE, or FUNCTRACE  options:
+   DEBUGMSG  - Enables output of various debug messages in the code
+   IOTRACE   - Enables output of CCW and other IO related traces
+   FUNCTRACE - Enables output of function entry/exit trace
+   Define any combination of above options to enable tracing
+
+   CLAW also uses the s390dbf file system  see claw_trace and claw_setup
+*/
+
+/* following enables tracing */
+//#define DEBUGMSG
+//#define IOTRACE
+//#define FUNCTRACE
+
+#ifdef DEBUGMSG
+#define DEBUG
+#endif
+
+#ifdef IOTRACE
+#define DEBUG
+#endif
+
+#ifdef FUNCTRACE
+#define DEBUG
+#endif
+
+ char debug_buffer[255];
+/**
+ * Debug Facility Stuff
+ */
+static debug_info_t *claw_dbf_setup;
+static debug_info_t *claw_dbf_trace;
+
+/**
+ *  CLAW Debug Facility functions
+ */
+static void
+claw_unregister_debug_facility(void)
+{
+       if (claw_dbf_setup)
+               debug_unregister(claw_dbf_setup);
+       if (claw_dbf_trace)
+               debug_unregister(claw_dbf_trace);
+}
+
+static int
+claw_register_debug_facility(void)
+{
+       claw_dbf_setup = debug_register("claw_setup", 1, 1, 8);
+       claw_dbf_trace = debug_register("claw_trace", 1, 2, 8);
+       if (claw_dbf_setup == NULL || claw_dbf_trace == NULL) {
+               printk(KERN_WARNING "Not enough memory for debug facility.\n");
+               claw_unregister_debug_facility();
+               return -ENOMEM;
+       }
+       debug_register_view(claw_dbf_setup, &debug_hex_ascii_view);
+       debug_set_level(claw_dbf_setup, 2);
+       debug_register_view(claw_dbf_trace, &debug_hex_ascii_view);
+       debug_set_level(claw_dbf_trace, 2);
+       return 0;
+}
+
+static inline void
+claw_set_busy(struct net_device *dev)
+{
+ ((struct claw_privbk *) dev->priv)->tbusy=1;
+ eieio();
+}
+
+static inline void
+claw_clear_busy(struct net_device *dev)
+{
+       clear_bit(0, &(((struct claw_privbk *) dev->priv)->tbusy));
+       netif_wake_queue(dev);
+       eieio();
+}
+
+static inline int
+claw_check_busy(struct net_device *dev)
+{
+       eieio();
+       return ((struct claw_privbk *) dev->priv)->tbusy;
+}
+
+static inline void
+claw_setbit_busy(int nr,struct net_device *dev)
+{
+       netif_stop_queue(dev);
+       set_bit(nr, (void *)&(((struct claw_privbk *)dev->priv)->tbusy));
+}
+
+static inline void
+claw_clearbit_busy(int nr,struct net_device *dev)
+{
+       clear_bit(nr,(void *)&(((struct claw_privbk *)dev->priv)->tbusy));
+       netif_wake_queue(dev);
+}
+
+static inline int
+claw_test_and_setbit_busy(int nr,struct net_device *dev)
+{
+       netif_stop_queue(dev);
+       return test_and_set_bit(nr,
+               (void *)&(((struct claw_privbk *) dev->priv)->tbusy));
+}
+
+
+/* Functions for the DEV methods */
+
+static int claw_probe(struct ccwgroup_device *cgdev);
+static void claw_remove_device(struct ccwgroup_device *cgdev);
+static void claw_purge_skb_queue(struct sk_buff_head *q);
+static int claw_new_device(struct ccwgroup_device *cgdev);
+static int claw_shutdown_device(struct ccwgroup_device *cgdev);
+static int claw_tx(struct sk_buff *skb, struct net_device *dev);
+static int claw_change_mtu( struct net_device *dev, int new_mtu);
+static int claw_open(struct net_device *dev);
+static void claw_irq_handler(struct ccw_device *cdev,
+       unsigned long intparm, struct irb *irb);
+static void claw_irq_tasklet ( unsigned long data );
+static int claw_release(struct net_device *dev);
+static void claw_write_retry ( struct chbk * p_ch );
+static void claw_write_next ( struct chbk * p_ch );
+static void claw_timer ( struct chbk * p_ch );
+
+/* Functions */
+static int add_claw_reads(struct net_device *dev,
+       struct ccwbk* p_first, struct ccwbk* p_last);
+static void inline ccw_check_return_code (struct ccw_device *cdev,
+        int return_code);
+static void inline ccw_check_unit_check (struct chbk * p_ch,
+       unsigned char sense );
+static int find_link(struct net_device *dev, char *host_name, char *ws_name );
+static int claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid);
+static int init_ccw_bk(struct net_device *dev);
+static void probe_error( struct ccwgroup_device *cgdev);
+static struct net_device_stats *claw_stats(struct net_device *dev);
+static int inline pages_to_order_of_mag(int num_of_pages);
+static struct sk_buff *claw_pack_skb(struct claw_privbk *privptr);
+#ifdef DEBUG
+static void dumpit (char *buf, int len);
+#endif
+/* sysfs Functions */
+static ssize_t claw_hname_show(struct device *dev, char *buf);
+static ssize_t claw_hname_write(struct device *dev,
+       const char *buf, size_t count);
+static ssize_t claw_adname_show(struct device *dev, char *buf);
+static ssize_t claw_adname_write(struct device *dev,
+       const char *buf, size_t count);
+static ssize_t claw_apname_show(struct device *dev, char *buf);
+static ssize_t claw_apname_write(struct device *dev,
+       const char *buf, size_t count);
+static ssize_t claw_wbuff_show(struct device *dev, char *buf);
+static ssize_t claw_wbuff_write(struct device *dev,
+       const char *buf, size_t count);
+static ssize_t claw_rbuff_show(struct device *dev, char *buf);
+static ssize_t claw_rbuff_write(struct device *dev,
+       const char *buf, size_t count);
+static int claw_add_files(struct device *dev);
+static void claw_remove_files(struct device *dev);
+
+/*   Functions for System Validate  */
+static int claw_process_control( struct net_device *dev, struct ccwbk * p_ccw);
+static int claw_send_control(struct net_device *dev, __u8 type, __u8 link,
+       __u8 correlator, __u8 rc , char *local_name, char *remote_name);
+static int claw_snd_conn_req(struct net_device *dev, __u8 link);
+static int claw_snd_disc(struct net_device *dev, struct clawctl * p_ctl);
+static int claw_snd_sys_validate_rsp(struct net_device *dev,
+        struct clawctl * p_ctl, __u32 return_code);
+static int claw_strt_conn_req(struct net_device *dev );
+static void claw_strt_read ( struct net_device *dev, int lock );
+static void claw_strt_out_IO( struct net_device *dev );
+static void claw_free_wrt_buf( struct net_device *dev );
+
+/* Functions for unpack reads   */
+static void unpack_read (struct net_device *dev );
+
+/* ccwgroup table  */
+
+static struct ccwgroup_driver claw_group_driver = {
+        .owner       = THIS_MODULE,
+        .name        = "claw",
+        .max_slaves  = 2,
+        .driver_id   = 0xC3D3C1E6,
+        .probe       = claw_probe,
+        .remove      = claw_remove_device,
+        .set_online  = claw_new_device,
+        .set_offline = claw_shutdown_device,
+};
+
+/*
+*
+*       Key functions
+*/
+
+/*----------------------------------------------------------------*
+ *   claw_probe                                                   *
+ *      this function is called for each CLAW device.             *
+ *----------------------------------------------------------------*/
+static int
+claw_probe(struct ccwgroup_device *cgdev)
+{
+       int             rc;
+       struct claw_privbk *privptr=NULL;
+
+#ifdef FUNCTRACE
+       printk(KERN_INFO "%s Enter\n",__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(2,setup,"probe");
+       if (!get_device(&cgdev->dev))
+               return -ENODEV;
+#ifdef DEBUGMSG
+        printk(KERN_INFO "claw: variable cgdev =\n");
+        dumpit((char *)cgdev, sizeof(struct ccwgroup_device));
+#endif
+       privptr = kmalloc(sizeof(struct claw_privbk), GFP_KERNEL);
+       if (privptr == NULL) {
+               probe_error(cgdev);
+               put_device(&cgdev->dev);
+               printk(KERN_WARNING "Out of memory %s %s Exit Line %d \n",
+                       cgdev->cdev[0]->dev.bus_id,__FUNCTION__,__LINE__);
+               CLAW_DBF_TEXT_(2,setup,"probex%d",-ENOMEM);
+               return -ENOMEM;
+       }
+       memset(privptr,0x00,sizeof(struct claw_privbk));
+       privptr->p_mtc_envelope= kmalloc( MAX_ENVELOPE_SIZE, GFP_KERNEL);
+       privptr->p_env = kmalloc(sizeof(struct claw_env), GFP_KERNEL);
+        if ((privptr->p_mtc_envelope==NULL) || (privptr->p_env==NULL)) {
+                probe_error(cgdev);
+               put_device(&cgdev->dev);
+               printk(KERN_WARNING "Out of memory %s %s Exit Line %d \n",
+                       cgdev->cdev[0]->dev.bus_id,__FUNCTION__,__LINE__);
+               CLAW_DBF_TEXT_(2,setup,"probex%d",-ENOMEM);
+                return -ENOMEM;
+        }
+       memset(privptr->p_mtc_envelope, 0x00, MAX_ENVELOPE_SIZE);
+       memset(privptr->p_env, 0x00, sizeof(struct claw_env));
+       memcpy(privptr->p_env->adapter_name,WS_NAME_NOT_DEF,8);
+       memcpy(privptr->p_env->host_name,WS_NAME_NOT_DEF,8);
+       memcpy(privptr->p_env->api_type,WS_NAME_NOT_DEF,8);
+       privptr->p_env->packing = 0;
+       privptr->p_env->write_buffers = 5;
+       privptr->p_env->read_buffers = 5;
+       privptr->p_env->read_size = CLAW_FRAME_SIZE;
+       privptr->p_env->write_size = CLAW_FRAME_SIZE;
+       rc = claw_add_files(&cgdev->dev);
+       if (rc) {
+               probe_error(cgdev);
+               put_device(&cgdev->dev);
+               printk(KERN_WARNING "add_files failed %s %s Exit Line %d \n",
+                       cgdev->cdev[0]->dev.bus_id,__FUNCTION__,__LINE__);
+               CLAW_DBF_TEXT_(2,setup,"probex%d",rc);
+               return rc;
+       }
+       printk(KERN_INFO "claw: sysfs files added for %s\n",cgdev->cdev[0]->dev.bus_id);
+       privptr->p_env->p_priv = privptr;
+        cgdev->cdev[0]->handler = claw_irq_handler;
+       cgdev->cdev[1]->handler = claw_irq_handler;
+       cgdev->dev.driver_data = privptr;
+#ifdef FUNCTRACE
+        printk(KERN_INFO "claw:%s exit on line %d, "
+               "rc = 0\n",__FUNCTION__,__LINE__);
+#endif
+       CLAW_DBF_TEXT(2,setup,"prbext 0");
+
+        return 0;
+}  /*  end of claw_probe       */
+
+/*-------------------------------------------------------------------*
+ *   claw_tx                                                         *
+ *-------------------------------------------------------------------*/
+
+static int
+claw_tx(struct sk_buff *skb, struct net_device *dev)
+{
+        int             rc;
+        struct claw_privbk *privptr=dev->priv;
+       unsigned long saveflags;
+        struct chbk *p_ch;
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s enter\n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"claw_tx");
+        p_ch=&privptr->channel[WRITE];
+        if (skb == NULL) {
+                printk(KERN_WARNING "%s: null pointer passed as sk_buffer\n",
+                       dev->name);
+                privptr->stats.tx_dropped++;
+#ifdef FUNCTRACE
+                printk(KERN_INFO "%s: %s() exit on line %d, rc = EIO\n",
+                       dev->name,__FUNCTION__, __LINE__);
+#endif
+               CLAW_DBF_TEXT_(2,trace,"clawtx%d",-EIO);
+                return -EIO;
+        }
+
+#ifdef IOTRACE
+        printk(KERN_INFO "%s: variable sk_buff=\n",dev->name);
+        dumpit((char *) skb, sizeof(struct sk_buff));
+        printk(KERN_INFO "%s: variable dev=\n",dev->name);
+        dumpit((char *) dev, sizeof(struct net_device));
+#endif
+        spin_lock_irqsave(get_ccwdev_lock(p_ch->cdev), saveflags);
+        rc=claw_hw_tx( skb, dev, 1 );
+        spin_unlock_irqrestore(get_ccwdev_lock(p_ch->cdev), saveflags);
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s exit on line %d, rc = %d\n",
+               dev->name, __FUNCTION__, __LINE__, rc);
+#endif
+       CLAW_DBF_TEXT_(4,trace,"clawtx%d",rc);
+        return rc;
+}   /*  end of claw_tx */
+
+/*------------------------------------------------------------------*
+ *  pack the collect queue into an skb and return it                *
+ *   If not packing just return the top skb from the queue          *
+ *------------------------------------------------------------------*/
+
+static struct sk_buff *
+claw_pack_skb(struct claw_privbk *privptr)
+{
+       struct sk_buff *new_skb,*held_skb;
+       struct chbk *p_ch = &privptr->channel[WRITE];
+       struct claw_env  *p_env = privptr->p_env;
+       int     pkt_cnt,pk_ind,so_far;
+
+       new_skb = NULL;         /* assume no dice */
+       pkt_cnt = 0;
+       CLAW_DBF_TEXT(4,trace,"PackSKBe");
+       if (skb_queue_len(&p_ch->collect_queue) > 0) {
+       /* some data */
+               held_skb = skb_dequeue(&p_ch->collect_queue);
+               if (p_env->packing != DO_PACKED)
+                       return held_skb;
+               if (held_skb)
+                       atomic_dec(&held_skb->users);
+               else
+                       return NULL;
+               /* get a new SKB we will pack at least one */
+               new_skb = dev_alloc_skb(p_env->write_size);
+               if (new_skb == NULL) {
+                       atomic_inc(&held_skb->users);
+                       skb_queue_head(&p_ch->collect_queue,held_skb);
+                       return NULL;
+               }
+               /* we have packed packet and a place to put it  */
+               pk_ind = 1;
+               so_far = 0;
+               new_skb->cb[1] = 'P'; /* every skb on queue has pack header */
+               while ((pk_ind) && (held_skb != NULL)) {
+                       if (held_skb->len+so_far <= p_env->write_size-8) {
+                               memcpy(skb_put(new_skb,held_skb->len),
+                                       held_skb->data,held_skb->len);
+                               privptr->stats.tx_packets++;
+                               so_far += held_skb->len;
+                               pkt_cnt++;
+                               dev_kfree_skb_irq(held_skb);
+                               held_skb = skb_dequeue(&p_ch->collect_queue);
+                               if (held_skb)
+                                       atomic_dec(&held_skb->users);
+                       } else {
+                               pk_ind = 0;
+                               atomic_inc(&held_skb->users);
+                               skb_queue_head(&p_ch->collect_queue,held_skb);
+                       }
+               }
+#ifdef IOTRACE
+               printk(KERN_INFO "%s: %s() Packed %d len %d\n",
+                       p_env->ndev->name,
+                       __FUNCTION__,pkt_cnt,new_skb->len);
+#endif
+       }
+       CLAW_DBF_TEXT(4,trace,"PackSKBx");
+       return new_skb;
+}
+
+/*-------------------------------------------------------------------*
+ *   claw_change_mtu                                                 *
+ *                                                                   *
+ *-------------------------------------------------------------------*/
+
+static int
+claw_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct claw_privbk  *privptr=dev->priv;
+       int buff_size;
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter  \n",dev->name,__FUNCTION__);
+#endif
+#ifdef DEBUGMSG
+        printk(KERN_INFO "variable dev =\n");
+        dumpit((char *) dev, sizeof(struct net_device));
+        printk(KERN_INFO "variable new_mtu = %d\n", new_mtu);
+#endif
+       CLAW_DBF_TEXT(4,trace,"setmtu");
+       buff_size = privptr->p_env->write_size;
+        if ((new_mtu < 60) || (new_mtu > buff_size)) {
+#ifdef FUNCTRACE
+                printk(KERN_INFO "%s:%s Exit on line %d, rc=EINVAL\n",
+               dev->name,
+               __FUNCTION__, __LINE__);
+#endif
+                return -EINVAL;
+        }
+        dev->mtu = new_mtu;
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Exit on line %d\n",dev->name,
+       __FUNCTION__, __LINE__);
+#endif
+        return 0;
+}  /*   end of claw_change_mtu */
+
+
+/*-------------------------------------------------------------------*
+ *   claw_open                                                       *
+ *                                                                   *
+ *-------------------------------------------------------------------*/
+static int
+claw_open(struct net_device *dev)
+{
+
+        int     rc;
+        int     i;
+        unsigned long       saveflags=0;
+        unsigned long       parm;
+        struct claw_privbk  *privptr;
+       DECLARE_WAITQUEUE(wait, current);
+        struct timer_list  timer;
+        struct ccwbk *p_buf;
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter  \n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"open");
+       if (!dev | (dev->name[0] == 0x00)) {
+               CLAW_DBF_TEXT(2,trace,"BadDev");
+               printk(KERN_WARNING "claw: Bad device at open failing \n");
+               return -ENODEV;
+       }
+       privptr = (struct claw_privbk *)dev->priv;
+        /*   allocate and initialize CCW blocks */
+       if (privptr->buffs_alloc == 0) {
+               rc=init_ccw_bk(dev);
+               if (rc) {
+                       printk(KERN_INFO "%s:%s Exit on line %d, rc=ENOMEM\n",
+                       dev->name,
+                       __FUNCTION__, __LINE__);
+                       CLAW_DBF_TEXT(2,trace,"openmem");
+                       return -ENOMEM;
+               }
+       }
+        privptr->system_validate_comp=0;
+        privptr->release_pend=0;
+       if(strncmp(privptr->p_env->api_type,WS_APPL_NAME_PACKED,6) == 0) {
+               privptr->p_env->read_size=DEF_PACK_BUFSIZE;
+               privptr->p_env->write_size=DEF_PACK_BUFSIZE;
+               privptr->p_env->packing=PACKING_ASK;
+       } else {
+               privptr->p_env->packing=0;
+               privptr->p_env->read_size=CLAW_FRAME_SIZE;
+               privptr->p_env->write_size=CLAW_FRAME_SIZE;
+       }
+        claw_set_busy(dev);
+       tasklet_init(&privptr->channel[READ].tasklet, claw_irq_tasklet,
+               (unsigned long) &privptr->channel[READ]);
+        for ( i = 0; i < 2;  i++) {
+               CLAW_DBF_TEXT_(2,trace,"opn_ch%d",i);
+                init_waitqueue_head(&privptr->channel[i].wait);
+               /* skb_queue_head_init(&p_ch->io_queue); */
+               if (i == WRITE)
+                       skb_queue_head_init(
+                               &privptr->channel[WRITE].collect_queue);
+                privptr->channel[i].flag_a = 0;
+                privptr->channel[i].IO_active = 0;
+                privptr->channel[i].flag  &= ~CLAW_TIMER;
+                init_timer(&timer);
+                timer.function = (void *)claw_timer;
+                timer.data = (unsigned long)(&privptr->channel[i]);
+                timer.expires = jiffies + 15*HZ;
+                add_timer(&timer);
+                spin_lock_irqsave(get_ccwdev_lock(
+                       privptr->channel[i].cdev), saveflags);
+                parm = (unsigned long) &privptr->channel[i];
+                privptr->channel[i].claw_state = CLAW_START_HALT_IO;
+               rc = 0;
+               add_wait_queue(&privptr->channel[i].wait, &wait);
+                rc = ccw_device_halt(
+                       (struct ccw_device *)privptr->channel[i].cdev,parm);
+                set_current_state(TASK_INTERRUPTIBLE);
+                spin_unlock_irqrestore(
+                       get_ccwdev_lock(privptr->channel[i].cdev), saveflags);
+                schedule();
+               set_current_state(TASK_RUNNING);
+                remove_wait_queue(&privptr->channel[i].wait, &wait);
+                if(rc != 0)
+                        ccw_check_return_code(privptr->channel[i].cdev, rc);
+                if((privptr->channel[i].flag & CLAW_TIMER) == 0x00)
+                        del_timer(&timer);
+        }
+        if ((((privptr->channel[READ].last_dstat |
+               privptr->channel[WRITE].last_dstat) &
+           ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) != 0x00) ||
+           (((privptr->channel[READ].flag |
+               privptr->channel[WRITE].flag) & CLAW_TIMER) != 0x00)) {
+#ifdef DEBUGMSG
+                printk(KERN_INFO "%s: channel problems during open - read:"
+                       " %02x -  write: %02x\n",
+                        dev->name,
+                       privptr->channel[READ].last_dstat,
+                       privptr->channel[WRITE].last_dstat);
+#endif
+                printk(KERN_INFO "%s: remote side is not ready\n", dev->name);
+               CLAW_DBF_TEXT(2,trace,"notrdy");
+
+                for ( i = 0; i < 2;  i++) {
+                        spin_lock_irqsave(
+                               get_ccwdev_lock(privptr->channel[i].cdev),
+                               saveflags);
+                        parm = (unsigned long) &privptr->channel[i];
+                        privptr->channel[i].claw_state = CLAW_STOP;
+                        rc = ccw_device_halt(
+                               (struct ccw_device *)&privptr->channel[i].cdev,
+                               parm);
+                        spin_unlock_irqrestore(
+                               get_ccwdev_lock(privptr->channel[i].cdev),
+                               saveflags);
+                        if (rc != 0) {
+                                ccw_check_return_code(
+                                       privptr->channel[i].cdev, rc);
+                        }
+                }
+                free_pages((unsigned long)privptr->p_buff_ccw,
+                       (int)pages_to_order_of_mag(privptr->p_buff_ccw_num));
+                if (privptr->p_env->read_size < PAGE_SIZE) {
+                        free_pages((unsigned long)privptr->p_buff_read,
+                              (int)pages_to_order_of_mag(
+                                       privptr->p_buff_read_num));
+                }
+                else {
+                        p_buf=privptr->p_read_active_first;
+                        while (p_buf!=NULL) {
+                                free_pages((unsigned long)p_buf->p_buffer,
+                                     (int)pages_to_order_of_mag(
+                                       privptr->p_buff_pages_perread ));
+                                p_buf=p_buf->next;
+                        }
+                }
+                if (privptr->p_env->write_size < PAGE_SIZE ) {
+                        free_pages((unsigned long)privptr->p_buff_write,
+                            (int)pages_to_order_of_mag(
+                               privptr->p_buff_write_num));
+                }
+                else {
+                        p_buf=privptr->p_write_active_first;
+                        while (p_buf!=NULL) {
+                                free_pages((unsigned long)p_buf->p_buffer,
+                                    (int)pages_to_order_of_mag(
+                                       privptr->p_buff_pages_perwrite ));
+                                p_buf=p_buf->next;
+                        }
+                }
+               privptr->buffs_alloc = 0;
+               privptr->channel[READ].flag= 0x00;
+               privptr->channel[WRITE].flag = 0x00;
+                privptr->p_buff_ccw=NULL;
+                privptr->p_buff_read=NULL;
+                privptr->p_buff_write=NULL;
+                claw_clear_busy(dev);
+#ifdef FUNCTRACE
+                printk(KERN_INFO "%s:%s Exit on line %d, rc=EIO\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+               CLAW_DBF_TEXT(2,trace,"open EIO");
+                return -EIO;
+        }
+
+        /*   Send SystemValidate command */
+
+        claw_clear_busy(dev);
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Exit on line %d, rc=0\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"openok");
+        return 0;
+}    /*     end of claw_open    */
+
+/*-------------------------------------------------------------------*
+*                                                                    *
+*       claw_irq_handler                                             *
+*                                                                    *
+*--------------------------------------------------------------------*/
+static void
+claw_irq_handler(struct ccw_device *cdev,
+       unsigned long intparm, struct irb *irb)
+{
+        struct chbk *p_ch = NULL;
+        struct claw_privbk *privptr = NULL;
+        struct net_device *dev = NULL;
+        struct claw_env  *p_env;
+        struct chbk *p_ch_r=NULL;
+
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s enter  \n",__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"clawirq");
+        /* Bypass all 'unsolicited interrupts' */
+       if (!cdev->dev.driver_data) {
+                printk(KERN_WARNING "claw: unsolicited interrupt for device:"
+                       "%s received c-%02x d-%02x\n",
+                        cdev->dev.bus_id,irb->scsw.cstat, irb->scsw.dstat);
+#ifdef FUNCTRACE
+                printk(KERN_INFO "claw: %s() "
+                       "exit on line %d\n",__FUNCTION__,__LINE__);
+#endif
+               CLAW_DBF_TEXT(2,trace,"badirq");
+                return;
+        }
+       privptr = (struct claw_privbk *)cdev->dev.driver_data;
+
+       /* Try to extract channel from driver data. */
+       if (privptr->channel[READ].cdev == cdev)
+               p_ch = &privptr->channel[READ];
+       else if (privptr->channel[WRITE].cdev == cdev)
+               p_ch = &privptr->channel[WRITE];
+       else {
+               printk(KERN_WARNING "claw: Can't determine channel for "
+                       "interrupt, device %s\n", cdev->dev.bus_id);
+               CLAW_DBF_TEXT(2,trace,"badchan");
+               return;
+       }
+       CLAW_DBF_TEXT_(4,trace,"IRQCH=%d",p_ch->flag);
+
+       dev = (struct net_device *) (p_ch->ndev);
+        p_env=privptr->p_env;
+
+#ifdef IOTRACE
+        printk(KERN_INFO "%s: interrupt for device: %04x "
+               "received c-%02x d-%02x state-%02x\n",
+                dev->name, p_ch->devno, irb->scsw.cstat,
+               irb->scsw.dstat, p_ch->claw_state);
+#endif
+
+       /* Copy interruption response block. */
+       memcpy(p_ch->irb, irb, sizeof(struct irb));
+
+        /* Check for good subchannel return code, otherwise error message */
+        if (irb->scsw.cstat  &&  !(irb->scsw.cstat & SCHN_STAT_PCI)) {
+                printk(KERN_INFO "%s: subchannel check for device: %04x -"
+                       " Sch Stat %02x  Dev Stat %02x CPA - %04x\n",
+                        dev->name, p_ch->devno,
+                       irb->scsw.cstat, irb->scsw.dstat,irb->scsw.cpa);
+#ifdef IOTRACE
+               dumpit((char *)irb,sizeof(struct irb));
+               dumpit((char *)(unsigned long)irb->scsw.cpa,
+                       sizeof(struct ccw1));
+#endif
+#ifdef FUNCTRACE
+               printk(KERN_INFO "%s:%s Exit on line %d\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+               CLAW_DBF_TEXT(2,trace,"chanchk");
+                /* return; */
+        }
+
+        /* Check the reason-code of a unit check */
+        if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+                ccw_check_unit_check(p_ch, irb->ecw[0]);
+        }
+
+        /* State machine to bring the connection up, down and to restart */
+        p_ch->last_dstat = irb->scsw.dstat;
+
+        switch (p_ch->claw_state) {
+                case CLAW_STOP:/* HALT_IO by claw_release (halt sequence) */
+#ifdef DEBUGMSG
+                        printk(KERN_INFO "%s: CLAW_STOP enter\n", dev->name);
+#endif
+                        if (!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
+                       (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
+                       (p_ch->irb->scsw.stctl ==
+                       (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+#ifdef FUNCTRACE
+                                printk(KERN_INFO "%s:%s Exit on line %d\n",
+                                       dev->name,__FUNCTION__,__LINE__);
+#endif
+                                return;
+                        }
+                        wake_up(&p_ch->wait);   /* wake up claw_release */
+
+#ifdef DEBUGMSG
+                        printk(KERN_INFO "%s: CLAW_STOP exit\n", dev->name);
+#endif
+#ifdef FUNCTRACE
+                        printk(KERN_INFO "%s:%s Exit on line %d\n",
+                               dev->name,__FUNCTION__,__LINE__);
+#endif
+                       CLAW_DBF_TEXT(4,trace,"stop");
+                        return;
+
+                case CLAW_START_HALT_IO: /* HALT_IO issued by claw_open  */
+#ifdef DEBUGMSG
+                        printk(KERN_INFO "%s: process CLAW_STAT_HALT_IO\n",
+                               dev->name);
+#endif
+                        if (!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
+                       (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
+                       (p_ch->irb->scsw.stctl ==
+                       (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+#ifdef FUNCTRACE
+                               printk(KERN_INFO "%s:%s Exit on line %d\n",
+                                       dev->name,__FUNCTION__,__LINE__);
+#endif
+                               CLAW_DBF_TEXT(4,trace,"haltio");
+                                return;
+                        }
+                        if (p_ch->flag == CLAW_READ) {
+                                p_ch->claw_state = CLAW_START_READ;
+                                wake_up(&p_ch->wait); /* wake claw_open (READ)*/
+                        }
+                       else
+                          if (p_ch->flag == CLAW_WRITE) {
+                                p_ch->claw_state = CLAW_START_WRITE;
+                                /*      send SYSTEM_VALIDATE                    */
+                                claw_strt_read(dev, LOCK_NO);
+                                       claw_send_control(dev,
+                                       SYSTEM_VALIDATE_REQUEST,
+                                       0, 0, 0,
+                                       p_env->host_name,
+                                       p_env->adapter_name );
+                        } else {
+                               printk(KERN_WARNING "claw: unsolicited "
+                                       "interrupt for device:"
+                                       "%s received c-%02x d-%02x\n",
+                                       cdev->dev.bus_id,
+                                       irb->scsw.cstat,
+                                       irb->scsw.dstat);
+                               return;
+                               }
+#ifdef DEBUGMSG
+                        printk(KERN_INFO "%s: process CLAW_STAT_HALT_IO exit\n",
+                               dev->name);
+#endif
+#ifdef FUNCTRACE
+                        printk(KERN_INFO "%s:%s Exit on line %d\n",
+                               dev->name,__FUNCTION__,__LINE__);
+#endif
+                       CLAW_DBF_TEXT(4,trace,"haltio");
+                        return;
+                case CLAW_START_READ:
+                       CLAW_DBF_TEXT(4,trace,"ReadIRQ");
+                        if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+                                clear_bit(0, (void *)&p_ch->IO_active);
+                                if ((p_ch->irb->ecw[0] & 0x41) == 0x41 ||
+                                    (p_ch->irb->ecw[0] & 0x40) == 0x40 ||
+                                    (p_ch->irb->ecw[0])        == 0)
+                                {
+                                        privptr->stats.rx_errors++;
+                                        printk(KERN_INFO "%s: Restart is "
+                                               "required after remote "
+                                               "side recovers \n",
+                                               dev->name);
+                                }
+#ifdef FUNCTRACE
+                               printk(KERN_INFO "%s:%s Exit on line %d\n",
+                                       dev->name,__FUNCTION__,__LINE__);
+#endif
+                                       CLAW_DBF_TEXT(4,trace,"notrdy");
+                                        return;
+                        }
+                        if ((p_ch->irb->scsw.cstat & SCHN_STAT_PCI) &&
+                           (p_ch->irb->scsw.dstat==0)) {
+                                if (test_and_set_bit(CLAW_BH_ACTIVE,
+                                       (void *)&p_ch->flag_a) == 0) {
+                                       tasklet_schedule(&p_ch->tasklet);
+                                }
+                               else {
+                                       CLAW_DBF_TEXT(4,trace,"PCINoBH");
+                               }
+#ifdef FUNCTRACE
+                               printk(KERN_INFO "%s:%s Exit on line %d\n",
+                                       dev->name,__FUNCTION__,__LINE__);
+#endif
+                               CLAW_DBF_TEXT(4,trace,"PCI_read");
+                                return;
+                        }
+                        if(!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
+                        (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
+                        (p_ch->irb->scsw.stctl ==
+                        (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+#ifdef FUNCTRACE
+                               printk(KERN_INFO "%s:%s Exit on line %d\n",
+                                       dev->name,__FUNCTION__,__LINE__);
+#endif
+                               CLAW_DBF_TEXT(4,trace,"SPend_rd");
+                                return;
+                        }
+                        clear_bit(0, (void *)&p_ch->IO_active);
+                        claw_clearbit_busy(TB_RETRY,dev);
+                        if (test_and_set_bit(CLAW_BH_ACTIVE,
+                               (void *)&p_ch->flag_a) == 0) {
+                               tasklet_schedule(&p_ch->tasklet);
+                         }
+                       else {
+                               CLAW_DBF_TEXT(4,trace,"RdBHAct");
+                       }
+
+#ifdef DEBUGMSG
+                        printk(KERN_INFO "%s: process CLAW_START_READ exit\n",
+                               dev->name);
+#endif
+#ifdef FUNCTRACE
+                       printk(KERN_INFO "%s:%s Exit on line %d\n",
+                               dev->name,__FUNCTION__,__LINE__);
+#endif
+                       CLAW_DBF_TEXT(4,trace,"RdIRQXit");
+                        return;
+                case CLAW_START_WRITE:
+                        if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+                                printk(KERN_INFO "%s: Unit Check Occured in "
+                                       "write channel\n",dev->name);
+                                clear_bit(0, (void *)&p_ch->IO_active);
+                                if (p_ch->irb->ecw[0] & 0x80 ) {
+                                        printk(KERN_INFO "%s: Resetting Event "
+                                               "occurred:\n",dev->name);
+                                        init_timer(&p_ch->timer);
+                                        p_ch->timer.function =
+                                               (void *)claw_write_retry;
+                                        p_ch->timer.data = (unsigned long)p_ch;
+                                        p_ch->timer.expires = jiffies + 10*HZ;
+                                        add_timer(&p_ch->timer);
+                                        printk(KERN_INFO "%s: write connection "
+                                               "restarting\n",dev->name);
+                                }
+#ifdef FUNCTRACE
+                               printk(KERN_INFO "%s:%s Exit on line %d\n",
+                                       dev->name,__FUNCTION__,__LINE__);
+#endif
+                               CLAW_DBF_TEXT(4,trace,"rstrtwrt");
+                                return;
+                        }
+                        if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) {
+                                        clear_bit(0, (void *)&p_ch->IO_active);
+                                        printk(KERN_INFO "%s: Unit Exception "
+                                               "Occured in write channel\n",
+                                               dev->name);
+                        }
+                        if(!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
+                       (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
+                       (p_ch->irb->scsw.stctl ==
+                       (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+#ifdef FUNCTRACE
+                               printk(KERN_INFO "%s:%s Exit on line %d\n",
+                                       dev->name,__FUNCTION__,__LINE__);
+#endif
+                               CLAW_DBF_TEXT(4,trace,"writeUE");
+                                return;
+                        }
+                        clear_bit(0, (void *)&p_ch->IO_active);
+                        if (claw_test_and_setbit_busy(TB_TX,dev)==0) {
+                                claw_write_next(p_ch);
+                                claw_clearbit_busy(TB_TX,dev);
+                                claw_clear_busy(dev);
+                        }
+                        p_ch_r=(struct chbk *)&privptr->channel[READ];
+                        if (test_and_set_bit(CLAW_BH_ACTIVE,
+                                       (void *)&p_ch_r->flag_a) == 0) {
+                               tasklet_schedule(&p_ch_r->tasklet);
+                        }
+
+#ifdef DEBUGMSG
+                        printk(KERN_INFO "%s: process CLAW_START_WRITE exit\n",
+                                dev->name);
+#endif
+#ifdef FUNCTRACE
+                       printk(KERN_INFO "%s:%s Exit on line %d\n",
+                               dev->name,__FUNCTION__,__LINE__);
+#endif
+                       CLAW_DBF_TEXT(4,trace,"StWtExit");
+                        return;
+                default:
+                        printk(KERN_WARNING "%s: wrong selection code - irq "
+                               "state=%d\n",dev->name,p_ch->claw_state);
+#ifdef FUNCTRACE
+                       printk(KERN_INFO "%s:%s Exit on line %d\n",
+                               dev->name,__FUNCTION__,__LINE__);
+#endif
+                       CLAW_DBF_TEXT(2,trace,"badIRQ");
+                        return;
+        }
+
+}       /*   end of claw_irq_handler    */
+
+
+/*-------------------------------------------------------------------*
+*       claw_irq_tasklet                                             *
+*                                                                    *
+*--------------------------------------------------------------------*/
+static void
+claw_irq_tasklet ( unsigned long data )
+{
+       struct chbk * p_ch;
+        struct net_device  *dev;
+        struct claw_privbk *       privptr;
+
+       p_ch = (struct chbk *) data;
+        dev = (struct net_device *)p_ch->ndev;
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter  \n",dev->name,__FUNCTION__);
+#endif
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: variable p_ch =\n",dev->name);
+        dumpit((char *) p_ch, sizeof(struct chbk));
+#endif
+       CLAW_DBF_TEXT(4,trace,"IRQtask");
+
+        privptr = (struct claw_privbk *) dev->priv;
+
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: bh routine - state-%02x\n" ,
+               dev->name, p_ch->claw_state);
+#endif
+
+        unpack_read(dev);
+        clear_bit(CLAW_BH_ACTIVE, (void *)&p_ch->flag_a);
+       CLAW_DBF_TEXT(4,trace,"TskletXt");
+#ifdef FUNCTRACE
+       printk(KERN_INFO "%s:%s Exit on line %d\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+        return;
+}       /*    end of claw_irq_bh    */
+
+/*-------------------------------------------------------------------*
+*       claw_release                                                 *
+*                                                                    *
+*--------------------------------------------------------------------*/
+static int
+claw_release(struct net_device *dev)
+{
+        int                rc;
+        int                i;
+        unsigned long      saveflags;
+        unsigned long      parm;
+        struct claw_privbk *privptr;
+        DECLARE_WAITQUEUE(wait, current);
+        struct ccwbk*             p_this_ccw;
+        struct ccwbk*             p_buf;
+
+       if (!dev)
+                return 0;
+        privptr = (struct claw_privbk *) dev->priv;
+        if (!privptr)
+                return 0;
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter  \n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"release");
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: variable dev =\n",dev->name);
+        dumpit((char *) dev, sizeof(struct net_device));
+       printk(KERN_INFO "Priv Buffalloc %d\n",privptr->buffs_alloc);
+       printk(KERN_INFO "Priv p_buff_ccw = %p\n",&privptr->p_buff_ccw);
+#endif
+        privptr->release_pend=1;
+        claw_setbit_busy(TB_STOP,dev);
+        for ( i = 1; i >=0 ;  i--) {
+                spin_lock_irqsave(
+                       get_ccwdev_lock(privptr->channel[i].cdev), saveflags);
+             /*   del_timer(&privptr->channel[READ].timer);  */
+               privptr->channel[i].claw_state = CLAW_STOP;
+                privptr->channel[i].IO_active = 0;
+                parm = (unsigned long) &privptr->channel[i];
+               if (i == WRITE)
+                       claw_purge_skb_queue(
+                               &privptr->channel[WRITE].collect_queue);
+                rc = ccw_device_halt (privptr->channel[i].cdev, parm);
+               if (privptr->system_validate_comp==0x00)  /* never opened? */
+                   init_waitqueue_head(&privptr->channel[i].wait);
+                add_wait_queue(&privptr->channel[i].wait, &wait);
+                set_current_state(TASK_INTERRUPTIBLE);
+               spin_unlock_irqrestore(
+                       get_ccwdev_lock(privptr->channel[i].cdev), saveflags);
+               schedule();
+               set_current_state(TASK_RUNNING);
+               remove_wait_queue(&privptr->channel[i].wait, &wait);
+               if (rc != 0) {
+                        ccw_check_return_code(privptr->channel[i].cdev, rc);
+                }
+        }
+       if (privptr->pk_skb != NULL) {
+               dev_kfree_skb(privptr->pk_skb);
+               privptr->pk_skb = NULL;
+       }
+       if(privptr->buffs_alloc != 1) {
+#ifdef FUNCTRACE
+       printk(KERN_INFO "%s:%s Exit on line %d\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+               CLAW_DBF_TEXT(4,trace,"none2fre");
+               return 0;
+       }
+       CLAW_DBF_TEXT(4,trace,"freebufs");
+       if (privptr->p_buff_ccw != NULL) {
+               free_pages((unsigned long)privptr->p_buff_ccw,
+                       (int)pages_to_order_of_mag(privptr->p_buff_ccw_num));
+       }
+       CLAW_DBF_TEXT(4,trace,"freeread");
+        if (privptr->p_env->read_size < PAGE_SIZE) {
+           if (privptr->p_buff_read != NULL) {
+                free_pages((unsigned long)privptr->p_buff_read,
+                     (int)pages_to_order_of_mag(privptr->p_buff_read_num));
+               }
+        }
+        else {
+                p_buf=privptr->p_read_active_first;
+                while (p_buf!=NULL) {
+                        free_pages((unsigned long)p_buf->p_buffer,
+                            (int)pages_to_order_of_mag(
+                               privptr->p_buff_pages_perread ));
+                        p_buf=p_buf->next;
+                }
+        }
+        CLAW_DBF_TEXT(4,trace,"freewrit");
+        if (privptr->p_env->write_size < PAGE_SIZE ) {
+                free_pages((unsigned long)privptr->p_buff_write,
+                     (int)pages_to_order_of_mag(privptr->p_buff_write_num));
+        }
+        else {
+                p_buf=privptr->p_write_active_first;
+                while (p_buf!=NULL) {
+                        free_pages((unsigned long)p_buf->p_buffer,
+                             (int)pages_to_order_of_mag(
+                             privptr->p_buff_pages_perwrite ));
+                        p_buf=p_buf->next;
+                }
+        }
+        CLAW_DBF_TEXT(4,trace,"clearptr");
+       privptr->buffs_alloc = 0;
+        privptr->p_buff_ccw=NULL;
+        privptr->p_buff_read=NULL;
+        privptr->p_buff_write=NULL;
+        privptr->system_validate_comp=0;
+        privptr->release_pend=0;
+        /*      Remove any writes that were pending and reset all reads   */
+        p_this_ccw=privptr->p_read_active_first;
+        while (p_this_ccw!=NULL) {
+                p_this_ccw->header.length=0xffff;
+                p_this_ccw->header.opcode=0xff;
+                p_this_ccw->header.flag=0x00;
+                p_this_ccw=p_this_ccw->next;
+        }
+
+        while (privptr->p_write_active_first!=NULL) {
+                p_this_ccw=privptr->p_write_active_first;
+                p_this_ccw->header.flag=CLAW_PENDING;
+                privptr->p_write_active_first=p_this_ccw->next;
+                p_this_ccw->next=privptr->p_write_free_chain;
+                privptr->p_write_free_chain=p_this_ccw;
+                ++privptr->write_free_count;
+        }
+        privptr->p_write_active_last=NULL;
+        privptr->mtc_logical_link = -1;
+        privptr->mtc_skipping = 1;
+        privptr->mtc_offset=0;
+
+        if (((privptr->channel[READ].last_dstat |
+               privptr->channel[WRITE].last_dstat) &
+               ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) != 0x00) {
+                printk(KERN_WARNING "%s: channel problems during close - "
+                       "read: %02x -  write: %02x\n",
+                dev->name,
+               privptr->channel[READ].last_dstat,
+               privptr->channel[WRITE].last_dstat);
+                CLAW_DBF_TEXT(2,trace,"badclose");
+        }
+#ifdef FUNCTRACE
+       printk(KERN_INFO "%s:%s Exit on line %d\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"rlsexit");
+        return 0;
+}      /* end of claw_release     */
+
+
+
+/*-------------------------------------------------------------------*
+*       claw_write_retry                                             *
+*                                                                    *
+*--------------------------------------------------------------------*/
+
+static void
+claw_write_retry ( struct chbk *p_ch )
+{
+
+        struct net_device  *dev=p_ch->ndev;
+
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
+        printk(KERN_INFO "claw: variable p_ch =\n");
+        dumpit((char *) p_ch, sizeof(struct chbk));
+#endif
+       CLAW_DBF_TEXT(4,trace,"w_retry");
+        if (p_ch->claw_state == CLAW_STOP) {
+#ifdef FUNCTRACE
+               printk(KERN_INFO "%s:%s Exit on line %d\n",
+                       dev->name,__FUNCTION__,__LINE__);
+#endif
+               return;
+        }
+#ifdef DEBUGMSG
+        printk( KERN_INFO "%s:%s  state-%02x\n" ,
+               dev->name,
+               __FUNCTION__,
+               p_ch->claw_state);
+#endif
+       claw_strt_out_IO( dev );
+#ifdef FUNCTRACE
+       printk(KERN_INFO "%s:%s Exit on line %d\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"rtry_xit");
+        return;
+}      /* end of claw_write_retry      */
+
+
+/*-------------------------------------------------------------------*
+*       claw_write_next                                              *
+*                                                                    *
+*--------------------------------------------------------------------*/
+
+static void
+claw_write_next ( struct chbk * p_ch )
+{
+
+        struct net_device  *dev;
+        struct claw_privbk *privptr=NULL;
+       struct sk_buff *pk_skb;
+       int     rc;
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter  \n",p_ch->ndev->name,__FUNCTION__);
+        printk(KERN_INFO "%s: variable p_ch =\n",p_ch->ndev->name);
+        dumpit((char *) p_ch, sizeof(struct chbk));
+#endif
+       CLAW_DBF_TEXT(4,trace,"claw_wrt");
+        if (p_ch->claw_state == CLAW_STOP)
+                return;
+        dev = (struct net_device *) p_ch->ndev;
+       privptr = (struct claw_privbk *) dev->priv;
+        claw_free_wrt_buf( dev );
+       if ((privptr->write_free_count > 0) &&
+           (skb_queue_len(&p_ch->collect_queue) > 0)) {
+               pk_skb = claw_pack_skb(privptr);
+               while (pk_skb != NULL) {
+                       rc = claw_hw_tx( pk_skb, dev,1);
+                       if (privptr->write_free_count > 0) {
+                               pk_skb = claw_pack_skb(privptr);
+                       } else
+                               pk_skb = NULL;
+               }
+       }
+        if (privptr->p_write_active_first!=NULL) {
+                claw_strt_out_IO(dev);
+        }
+
+#ifdef FUNCTRACE
+       printk(KERN_INFO "%s:%s Exit on line %d\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+        return;
+}      /* end of claw_write_next      */
+
+/*-------------------------------------------------------------------*
+*                                                                    *
+*       claw_timer                                                   *
+*--------------------------------------------------------------------*/
+
+static void
+claw_timer ( struct chbk * p_ch )
+{
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Entry\n",p_ch->ndev->name,__FUNCTION__);
+        printk(KERN_INFO "%s: variable p_ch =\n",p_ch->ndev->name);
+        dumpit((char *) p_ch, sizeof(struct chbk));
+#endif
+       CLAW_DBF_TEXT(4,trace,"timer");
+        p_ch->flag |= CLAW_TIMER;
+        wake_up(&p_ch->wait);
+#ifdef FUNCTRACE
+       printk(KERN_INFO "%s:%s Exit on line %d\n",
+               p_ch->ndev->name,__FUNCTION__,__LINE__);
+#endif
+        return;
+}      /* end of claw_timer  */
+
+
+/*
+*
+*       functions
+*/
+
+
+/*-------------------------------------------------------------------*
+*                                                                    *
+*     pages_to_order_of_mag                                          *
+*                                                                    *
+*    takes a number of pages from 1 to 512 and returns the           *
+*    log(num_pages)/log(2) get_free_pages() needs a base 2 order     *
+*    of magnitude get_free_pages() has an upper order of 9           *
+*--------------------------------------------------------------------*/
+
+static int inline
+pages_to_order_of_mag(int num_of_pages)
+{
+       int     order_of_mag=1;         /* assume 2 pages */
+       int     nump=2;
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s Enter pages = %d \n",__FUNCTION__,num_of_pages);
+#endif
+       CLAW_DBF_TEXT_(5,trace,"pages%d",num_of_pages);
+       if (num_of_pages == 1)   {return 0; }  /* magnitude of 0 = 1 page */
+       /* 512 pages = 2Meg on 4k page systems */
+       if (num_of_pages >= 512) {return 9; }
+       /* we have two or more pages order is at least 1 */
+       for (nump=2 ;nump <= 512;nump*=2) {
+         if (num_of_pages <= nump)
+                 break;
+         order_of_mag +=1;
+       }
+       if (order_of_mag > 9) { order_of_mag = 9; }  /* I know it's paranoid */
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s Exit on line %d, order = %d\n",
+       __FUNCTION__,__LINE__, order_of_mag);
+#endif
+       CLAW_DBF_TEXT_(5,trace,"mag%d",order_of_mag);
+       return order_of_mag;
+}
+
+/*-------------------------------------------------------------------*
+*                                                                    *
+*     add_claw_reads                                                 *
+*                                                                    *
+*--------------------------------------------------------------------*/
+static int
+add_claw_reads(struct net_device *dev, struct ccwbk* p_first,
+       struct ccwbk* p_last)
+{
+        struct claw_privbk *privptr;
+        struct ccw1  temp_ccw;
+        struct endccw * p_end;
+#ifdef IOTRACE
+        struct ccwbk*  p_buf;
+#endif
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter  \n",dev->name,__FUNCTION__);
+#endif
+#ifdef DEBUGMSG
+        printk(KERN_INFO "dev\n");
+        dumpit((char *) dev, sizeof(struct net_device));
+        printk(KERN_INFO "p_first\n");
+        dumpit((char *) p_first, sizeof(struct ccwbk));
+        printk(KERN_INFO "p_last\n");
+        dumpit((char *) p_last, sizeof(struct ccwbk));
+#endif
+       CLAW_DBF_TEXT(4,trace,"addreads");
+        privptr = dev->priv;
+        p_end = privptr->p_end_ccw;
+
+        /* first CCW and last CCW contains a new set of read channel programs
+        *       to apend the running channel programs
+        */
+        if ( p_first==NULL) {
+#ifdef FUNCTRACE
+               printk(KERN_INFO "%s:%s Exit on line %d\n",
+                       dev->name,__FUNCTION__,__LINE__);
+#endif
+               CLAW_DBF_TEXT(4,trace,"addexit");
+                return 0;
+        }
+
+        /* set up ending CCW sequence for this segment */
+        if (p_end->read1) {
+                p_end->read1=0x00;    /*  second ending CCW is now active */
+                /*      reset ending CCWs and setup TIC CCWs              */
+                p_end->read2_nop2.cmd_code = CCW_CLAW_CMD_READFF;
+                p_end->read2_nop2.flags  = CCW_FLAG_SLI | CCW_FLAG_SKIP;
+                p_last->r_TIC_1.cda =(__u32)__pa(&p_end->read2_nop1);
+                p_last->r_TIC_2.cda =(__u32)__pa(&p_end->read2_nop1);
+                p_end->read2_nop2.cda=0;
+                p_end->read2_nop2.count=1;
+        }
+        else {
+                p_end->read1=0x01;  /* first ending CCW is now active */
+                /*      reset ending CCWs and setup TIC CCWs          */
+                p_end->read1_nop2.cmd_code = CCW_CLAW_CMD_READFF;
+                p_end->read1_nop2.flags  = CCW_FLAG_SLI | CCW_FLAG_SKIP;
+                p_last->r_TIC_1.cda = (__u32)__pa(&p_end->read1_nop1);
+                p_last->r_TIC_2.cda = (__u32)__pa(&p_end->read1_nop1);
+                p_end->read1_nop2.cda=0;
+                p_end->read1_nop2.count=1;
+        }
+
+        if ( privptr-> p_read_active_first ==NULL ) {
+#ifdef DEBUGMSG
+                printk(KERN_INFO "%s:%s p_read_active_frist == NULL \n",
+                       dev->name,__FUNCTION__);
+                printk(KERN_INFO "%s:%s Read active first/last changed \n",
+                       dev->name,__FUNCTION__);
+#endif
+                privptr-> p_read_active_first= p_first;  /*    set new first */
+                privptr-> p_read_active_last = p_last;   /*    set new last  */
+        }
+        else {
+
+#ifdef DEBUGMSG
+                printk(KERN_INFO "%s:%s Read in progress \n",
+               dev->name,__FUNCTION__);
+#endif
+                /* set up TIC ccw  */
+                temp_ccw.cda= (__u32)__pa(&p_first->read);
+                temp_ccw.count=0;
+                temp_ccw.flags=0;
+                temp_ccw.cmd_code = CCW_CLAW_CMD_TIC;
+
+
+                if (p_end->read1) {
+
+               /* first set of CCW's is chained to the new read              */
+               /* chain, so the second set is chained to the active chain.   */
+               /* Therefore modify the second set to point to the new        */
+               /* read chain set up TIC CCWs                                 */
+               /* make sure we update the CCW so channel doesn't fetch it    */
+               /* when it's only half done                                   */
+                        memcpy( &p_end->read2_nop2, &temp_ccw ,
+                               sizeof(struct ccw1));
+                        privptr->p_read_active_last->r_TIC_1.cda=
+                               (__u32)__pa(&p_first->read);
+                        privptr->p_read_active_last->r_TIC_2.cda=
+                               (__u32)__pa(&p_first->read);
+                }
+                else {
+                        /* make sure we update the CCW so channel doesn't   */
+                       /* fetch it when it is only half done               */
+                        memcpy( &p_end->read1_nop2, &temp_ccw ,
+                               sizeof(struct ccw1));
+                        privptr->p_read_active_last->r_TIC_1.cda=
+                               (__u32)__pa(&p_first->read);
+                        privptr->p_read_active_last->r_TIC_2.cda=
+                               (__u32)__pa(&p_first->read);
+                }
+                /*      chain in new set of blocks                              */
+                privptr->p_read_active_last->next = p_first;
+                privptr->p_read_active_last=p_last;
+        } /* end of if ( privptr-> p_read_active_first ==NULL)  */
+#ifdef IOTRACE
+        printk(KERN_INFO "%s:%s  dump p_last CCW BK \n",dev->name,__FUNCTION__);
+        dumpit((char *)p_last, sizeof(struct ccwbk));
+        printk(KERN_INFO "%s:%s  dump p_end CCW BK \n",dev->name,__FUNCTION__);
+        dumpit((char *)p_end, sizeof(struct endccw));
+
+        printk(KERN_INFO "%s:%s dump p_first CCW BK \n",dev->name,__FUNCTION__);
+        dumpit((char *)p_first, sizeof(struct ccwbk));
+        printk(KERN_INFO "%s:%s Dump Active CCW chain \n",
+               dev->name,__FUNCTION__);
+        p_buf=privptr->p_read_active_first;
+        while (p_buf!=NULL) {
+                dumpit((char *)p_buf, sizeof(struct ccwbk));
+                p_buf=p_buf->next;
+        }
+#endif
+#ifdef FUNCTRACE
+       printk(KERN_INFO "%s:%s Exit on line %d\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"addexit");
+        return 0;
+}    /*     end of add_claw_reads   */
+
+/*-------------------------------------------------------------------*
+ *   ccw_check_return_code                                           *
+ *                                                                   *
+ *-------------------------------------------------------------------*/
+
+static void inline
+ccw_check_return_code(struct ccw_device *cdev, int return_code)
+{
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s: %s() > enter  \n",
+               cdev->dev.bus_id,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"ccwret");
+#ifdef DEBUGMSG
+        printk(KERN_INFO "variable cdev =\n");
+        dumpit((char *) cdev, sizeof(struct ccw_device));
+        printk(KERN_INFO "variable return_code = %d\n",return_code);
+#endif
+        if (return_code != 0) {
+                switch (return_code) {
+                        case -EBUSY:
+                                printk(KERN_INFO "%s: Busy !\n",
+                                       cdev->dev.bus_id);
+                                break;
+                        case -ENODEV:
+                                printk(KERN_EMERG "%s: Missing device called "
+                                       "for IO ENODEV\n", cdev->dev.bus_id);
+                                break;
+                        case -EIO:
+                                printk(KERN_EMERG "%s: Status pending... EIO \n",
+                                       cdev->dev.bus_id);
+                                break;
+                       case -EINVAL:
+                                printk(KERN_EMERG "%s: Invalid Dev State EINVAL \n",
+                                       cdev->dev.bus_id);
+                                break;
+                        default:
+                                printk(KERN_EMERG "%s: Unknown error in "
+                                "Do_IO %d\n",cdev->dev.bus_id, return_code);
+                }
+        }
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s: %s() > exit on line %d\n",
+               cdev->dev.bus_id,__FUNCTION__,__LINE__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"ccwret");
+}    /*    end of ccw_check_return_code   */
+
+/*-------------------------------------------------------------------*
+*       ccw_check_unit_check                                         *
+*--------------------------------------------------------------------*/
+
+static void inline
+ccw_check_unit_check(struct chbk * p_ch, unsigned char sense )
+{
+       struct net_device *dev = p_ch->ndev;
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s: %s() > enter\n",dev->name,__FUNCTION__);
+#endif
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: variable dev =\n",dev->name);
+        dumpit((char *)dev, sizeof(struct net_device));
+        printk(KERN_INFO "%s: variable sense =\n",dev->name);
+        dumpit((char *)&sense, 2);
+#endif
+       CLAW_DBF_TEXT(4,trace,"unitchek");
+
+        printk(KERN_INFO "%s: Unit Check with sense byte:0x%04x\n",
+                dev->name, sense);
+
+        if (sense & 0x40) {
+                if (sense & 0x01) {
+                        printk(KERN_WARNING "%s: Interface disconnect or "
+                               "Selective reset "
+                               "occurred (remote side)\n", dev->name);
+                }
+                else {
+                        printk(KERN_WARNING "%s: System reset occured"
+                               " (remote side)\n", dev->name);
+                }
+        }
+        else if (sense & 0x20) {
+                if (sense & 0x04) {
+                        printk(KERN_WARNING "%s: Data-streaming "
+                               "timeout)\n", dev->name);
+                }
+                else  {
+                        printk(KERN_WARNING "%s: Data-transfer parity"
+                               " error\n", dev->name);
+                }
+        }
+        else if (sense & 0x10) {
+                if (sense & 0x20) {
+                        printk(KERN_WARNING "%s: Hardware malfunction "
+                               "(remote side)\n", dev->name);
+                }
+                else {
+                        printk(KERN_WARNING "%s: read-data parity error "
+                               "(remote side)\n", dev->name);
+                }
+        }
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s: %s() exit on line %d\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+}   /*    end of ccw_check_unit_check    */
+
+
+
+/*-------------------------------------------------------------------*
+* Dump buffer format                                                 *
+*                                                                    *
+*--------------------------------------------------------------------*/
+#ifdef DEBUG
+static void
+dumpit(char* buf, int len)
+{
+
+        __u32      ct, sw, rm, dup;
+        char       *ptr, *rptr;
+        char       tbuf[82], tdup[82];
+#if (CONFIG_ARCH_S390X)
+        char       addr[22];
+#else
+        char       addr[12];
+#endif
+        char       boff[12];
+        char       bhex[82], duphex[82];
+        char       basc[40];
+
+        sw  = 0;
+        rptr =ptr=buf;
+        rm  = 16;
+        duphex[0]  = 0x00;
+        dup = 0;
+        for ( ct=0; ct < len; ct++, ptr++, rptr++ )  {
+                if (sw == 0) {
+#if (CONFIG_ARCH_S390X)
+                        sprintf(addr, "%16.16lX",(unsigned long)rptr);
+#else
+                        sprintf(addr, "%8.8X",(__u32)rptr);
+#endif
+                        sprintf(boff, "%4.4X", (__u32)ct);
+                        bhex[0] = '\0';
+                        basc[0] = '\0';
+                }
+                if ((sw == 4) || (sw == 12)) {
+                        strcat(bhex, " ");
+                }
+                if (sw == 8) {
+                        strcat(bhex, "  ");
+                }
+#if (CONFIG_ARCH_S390X)
+                sprintf(tbuf,"%2.2lX", (unsigned long)*ptr);
+#else
+                sprintf(tbuf,"%2.2X", (__u32)*ptr);
+#endif
+                tbuf[2] = '\0';
+                strcat(bhex, tbuf);
+                if ((0!=isprint(*ptr)) && (*ptr >= 0x20)) {
+                        basc[sw] = *ptr;
+                }
+                else {
+                        basc[sw] = '.';
+                }
+                basc[sw+1] = '\0';
+                sw++;
+                rm--;
+                if (sw==16) {
+                        if ((strcmp(duphex, bhex)) !=0) {
+                                if (dup !=0) {
+                                       sprintf(tdup,"Duplicate as above to"
+                                               " %s", addr);
+                                        printk( KERN_INFO "                 "
+                                               "   --- %s ---\n",tdup);
+                                }
+                                printk( KERN_INFO "   %s (+%s) : %s  [%s]\n",
+                                        addr, boff, bhex, basc);
+                                dup = 0;
+                                strcpy(duphex, bhex);
+                        }
+                        else {
+                                dup++;
+                        }
+                        sw = 0;
+                        rm = 16;
+                }
+        }  /* endfor */
+
+        if (sw != 0) {
+                for ( ; rm > 0; rm--, sw++ ) {
+                        if ((sw==4) || (sw==12)) strcat(bhex, " ");
+                        if (sw==8)               strcat(bhex, "  ");
+                        strcat(bhex, "  ");
+                        strcat(basc, " ");
+                }
+                if (dup !=0) {
+                        sprintf(tdup,"Duplicate as above to %s", addr);
+                        printk( KERN_INFO "                    --- %s ---\n",
+                               tdup);
+                }
+                printk( KERN_INFO "   %s (+%s) : %s  [%s]\n",
+                       addr, boff, bhex, basc);
+        }
+        else {
+                if (dup >=1) {
+                        sprintf(tdup,"Duplicate as above to %s", addr);
+                        printk( KERN_INFO "                    --- %s ---\n",
+                               tdup);
+                }
+                if (dup !=0) {
+                        printk( KERN_INFO "   %s (+%s) : %s  [%s]\n",
+                               addr, boff, bhex, basc);
+                }
+        }
+        return;
+
+}   /*   end of dumpit  */
+#endif
+
+/*-------------------------------------------------------------------*
+*               find_link                                            *
+*--------------------------------------------------------------------*/
+static int
+find_link(struct net_device *dev, char *host_name, char *ws_name )
+{
+       struct claw_privbk *privptr;
+       struct claw_env *p_env;
+       int    rc=0;
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s > enter  \n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(2,setup,"findlink");
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: variable dev = \n",dev->name);
+        dumpit((char *) dev, sizeof(struct net_device));
+        printk(KERN_INFO "%s: variable host_name = %s\n",dev->name, host_name);
+        printk(KERN_INFO "%s: variable ws_name = %s\n",dev->name, ws_name);
+#endif
+        privptr=dev->priv;
+        p_env=privptr->p_env;
+       switch (p_env->packing)
+       {
+               case  PACKING_ASK:
+                       if ((memcmp(WS_APPL_NAME_PACKED, host_name, 8)!=0) ||
+                           (memcmp(WS_APPL_NAME_PACKED, ws_name, 8)!=0 ))
+                            rc = EINVAL;
+                       break;
+               case  DO_PACKED:
+               case  PACK_SEND:
+                       if ((memcmp(WS_APPL_NAME_IP_NAME, host_name, 8)!=0) ||
+                           (memcmp(WS_APPL_NAME_IP_NAME, ws_name, 8)!=0 ))
+                               rc = EINVAL;
+                       break;
+               default:
+                       if ((memcmp(HOST_APPL_NAME, host_name, 8)!=0) ||
+                           (memcmp(p_env->api_type , ws_name, 8)!=0))
+                               rc = EINVAL;
+                       break;
+       }
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Exit on line %d\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+        return 0;
+}    /*    end of find_link    */
+
+/*-------------------------------------------------------------------*
+ *   claw_hw_tx                                                      *
+ *                                                                   *
+ *                                                                   *
+ *-------------------------------------------------------------------*/
+
+static int
+claw_hw_tx(struct sk_buff *skb, struct net_device *dev, long linkid)
+{
+        int                             rc=0;
+        struct claw_privbk             *privptr;
+        struct ccwbk           *p_this_ccw;
+        struct ccwbk           *p_first_ccw;
+        struct ccwbk           *p_last_ccw;
+        __u32                           numBuffers;
+        signed long                     len_of_data;
+        unsigned long                   bytesInThisBuffer;
+        unsigned char                   *pDataAddress;
+        struct endccw                   *pEnd;
+        struct ccw1                     tempCCW;
+        struct chbk                     *p_ch;
+       struct claw_env                 *p_env;
+        int                             lock;
+       struct clawph                   *pk_head;
+       struct chbk                     *ch;
+#ifdef IOTRACE
+        struct ccwbk                   *p_buf;
+#endif
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s: %s() > enter\n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"hw_tx");
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: variable dev skb =\n",dev->name);
+        dumpit((char *) skb, sizeof(struct sk_buff));
+        printk(KERN_INFO "%s: variable dev =\n",dev->name);
+        dumpit((char *) dev, sizeof(struct net_device));
+        printk(KERN_INFO "%s: variable linkid = %ld\n",dev->name,linkid);
+#endif
+        privptr = (struct claw_privbk *) (dev->priv);
+        p_ch=(struct chbk *)&privptr->channel[WRITE];
+       p_env =privptr->p_env;
+#ifdef IOTRACE
+        printk(KERN_INFO "%s: %s() dump sk_buff  \n",dev->name,__FUNCTION__);
+        dumpit((char *)skb ,sizeof(struct sk_buff));
+#endif
+       claw_free_wrt_buf(dev); /* Clean up free chain if posible */
+        /*  scan the write queue to free any completed write packets   */
+        p_first_ccw=NULL;
+        p_last_ccw=NULL;
+       if ((p_env->packing >= PACK_SEND) &&
+                   (skb->cb[1] != 'P')) {
+               skb_push(skb,sizeof(struct clawph));
+               pk_head=(struct clawph *)skb->data;
+               pk_head->len=skb->len-sizeof(struct clawph);
+               if (pk_head->len%4)  {
+                       pk_head->len+= 4-(pk_head->len%4);
+                       skb_pad(skb,4-(pk_head->len%4));
+                       skb_put(skb,4-(pk_head->len%4));
+               }
+               if (p_env->packing == DO_PACKED)
+                       pk_head->link_num = linkid;
+               else
+                       pk_head->link_num = 0;
+               pk_head->flag = 0x00;
+               skb_pad(skb,4);
+               skb->cb[1] = 'P';
+       }
+        if (linkid == 0) {
+               if (claw_check_busy(dev)) {
+                       if (privptr->write_free_count!=0) {
+                                claw_clear_busy(dev);
+                        }
+                        else {
+                                claw_strt_out_IO(dev );
+                                claw_free_wrt_buf( dev );
+                                if (privptr->write_free_count==0) {
+#ifdef IOTRACE
+                                       printk(KERN_INFO "%s: "
+                                          "(claw_check_busy) no free write "
+                                          "buffers\n", dev->name);
+#endif
+                                       ch = &privptr->channel[WRITE];
+                                       atomic_inc(&skb->users);
+                                       skb_queue_tail(&ch->collect_queue, skb);
+                                       goto Done;
+                                }
+                                else {
+                                       claw_clear_busy(dev);
+                                }
+                        }
+                }
+                /*  tx lock  */
+                if (claw_test_and_setbit_busy(TB_TX,dev)) { /* set to busy */
+#ifdef DEBUGMSG
+                        printk(KERN_INFO "%s:  busy  (claw_test_and_setbit_"
+                               "busy)\n", dev->name);
+#endif
+                       ch = &privptr->channel[WRITE];
+                       atomic_inc(&skb->users);
+                       skb_queue_tail(&ch->collect_queue, skb);
+                        claw_strt_out_IO(dev );
+                        rc=-EBUSY;
+                        goto Done2;
+                }
+        }
+        /*      See how many write buffers are required to hold this data */
+        numBuffers= ( skb->len + privptr->p_env->write_size - 1) /
+                       ( privptr->p_env->write_size);
+
+        /*      If that number of buffers isn't available, give up for now */
+        if (privptr->write_free_count < numBuffers ||
+            privptr->p_write_free_chain == NULL ) {
+
+                claw_setbit_busy(TB_NOBUFFER,dev);
+
+#ifdef DEBUGMSG
+                printk(KERN_INFO "%s:  busy  (claw_setbit_busy"
+                       "(TB_NOBUFFER))\n", dev->name);
+                printk(KERN_INFO "       free_count: %d, numBuffers : %d\n",
+                       (int)privptr->write_free_count,(int) numBuffers );
+#endif
+               ch = &privptr->channel[WRITE];
+               atomic_inc(&skb->users);
+               skb_queue_tail(&ch->collect_queue, skb);
+               CLAW_DBF_TEXT(2,trace,"clawbusy");
+                goto Done2;
+        }
+        pDataAddress=skb->data;
+        len_of_data=skb->len;
+
+        while (len_of_data > 0) {
+#ifdef DEBUGMSG
+                printk(KERN_INFO "%s: %s() length-of-data is %ld \n",
+                       dev->name ,__FUNCTION__,len_of_data);
+                dumpit((char *)pDataAddress ,64);
+#endif
+                p_this_ccw=privptr->p_write_free_chain;  /* get a block */
+               if (p_this_ccw == NULL) { /* lost the race */
+                       ch = &privptr->channel[WRITE];
+                       atomic_inc(&skb->users);
+                       skb_queue_tail(&ch->collect_queue, skb);
+                       goto Done2;
+               }
+                privptr->p_write_free_chain=p_this_ccw->next;
+                p_this_ccw->next=NULL;
+                --privptr->write_free_count; /* -1 */
+                bytesInThisBuffer=len_of_data;
+                memcpy( p_this_ccw->p_buffer,pDataAddress, bytesInThisBuffer);
+                len_of_data-=bytesInThisBuffer;
+                pDataAddress+=(unsigned long)bytesInThisBuffer;
+                /*      setup write CCW         */
+                p_this_ccw->write.cmd_code = (linkid * 8) +1;
+                if (len_of_data>0) {
+                        p_this_ccw->write.cmd_code+=MORE_to_COME_FLAG;
+                }
+                p_this_ccw->write.count=bytesInThisBuffer;
+                /*      now add to end of this chain    */
+                if (p_first_ccw==NULL)    {
+                        p_first_ccw=p_this_ccw;
+                }
+                if (p_last_ccw!=NULL) {
+                        p_last_ccw->next=p_this_ccw;
+                        /*      set up TIC ccws         */
+                        p_last_ccw->w_TIC_1.cda=
+                               (__u32)__pa(&p_this_ccw->write);
+                }
+                p_last_ccw=p_this_ccw;      /* save new last block */
+#ifdef IOTRACE
+               printk(KERN_INFO "%s: %s() > CCW and Buffer %ld bytes long \n",
+                       dev->name,__FUNCTION__,bytesInThisBuffer);
+                dumpit((char *)p_this_ccw, sizeof(struct ccwbk));
+                dumpit((char *)p_this_ccw->p_buffer, 64);
+#endif
+        }
+
+        /*      FirstCCW and LastCCW now contain a new set of write channel
+        *       programs to append to the running channel program
+        */
+
+        if (p_first_ccw!=NULL) {
+                /*      setup ending ccw sequence for this segment              */
+                pEnd=privptr->p_end_ccw;
+                if (pEnd->write1) {
+                        pEnd->write1=0x00;   /* second end ccw is now active */
+                        /*      set up Tic CCWs         */
+                        p_last_ccw->w_TIC_1.cda=
+                               (__u32)__pa(&pEnd->write2_nop1);
+                        pEnd->write2_nop2.cmd_code = CCW_CLAW_CMD_READFF;
+                        pEnd->write2_nop2.flags    =
+                               CCW_FLAG_SLI | CCW_FLAG_SKIP;
+                        pEnd->write2_nop2.cda=0;
+                        pEnd->write2_nop2.count=1;
+                }
+                else {  /*  end of if (pEnd->write1)*/
+                        pEnd->write1=0x01;   /* first end ccw is now active */
+                        /*      set up Tic CCWs         */
+                        p_last_ccw->w_TIC_1.cda=
+                               (__u32)__pa(&pEnd->write1_nop1);
+                        pEnd->write1_nop2.cmd_code = CCW_CLAW_CMD_READFF;
+                        pEnd->write1_nop2.flags    =
+                               CCW_FLAG_SLI | CCW_FLAG_SKIP;
+                        pEnd->write1_nop2.cda=0;
+                        pEnd->write1_nop2.count=1;
+                }  /* end if if (pEnd->write1) */
+
+
+                if (privptr->p_write_active_first==NULL ) {
+                        privptr->p_write_active_first=p_first_ccw;
+                        privptr->p_write_active_last=p_last_ccw;
+                }
+                else {
+
+                        /*      set up Tic CCWs         */
+
+                        tempCCW.cda=(__u32)__pa(&p_first_ccw->write);
+                        tempCCW.count=0;
+                        tempCCW.flags=0;
+                        tempCCW.cmd_code=CCW_CLAW_CMD_TIC;
+
+                        if (pEnd->write1) {
+
+                 /*
+                 * first set of ending CCW's is chained to the new write
+                 * chain, so the second set is chained to the active chain
+                 * Therefore modify the second set to point the new write chain.
+                 * make sure we update the CCW atomically
+                 * so channel does not fetch it when it's only half done
+                 */
+                                memcpy( &pEnd->write2_nop2, &tempCCW ,
+                                       sizeof(struct ccw1));
+                                privptr->p_write_active_last->w_TIC_1.cda=
+                                       (__u32)__pa(&p_first_ccw->write);
+                        }
+                        else {
+
+                        /*make sure we update the CCW atomically
+                         *so channel does not fetch it when it's only half done
+                         */
+                                memcpy(&pEnd->write1_nop2, &tempCCW ,
+                                       sizeof(struct ccw1));
+                                privptr->p_write_active_last->w_TIC_1.cda=
+                                       (__u32)__pa(&p_first_ccw->write);
+
+                        } /* end if if (pEnd->write1) */
+
+                        privptr->p_write_active_last->next=p_first_ccw;
+                        privptr->p_write_active_last=p_last_ccw;
+                }
+
+        } /* endif (p_first_ccw!=NULL)  */
+
+
+#ifdef IOTRACE
+        printk(KERN_INFO "%s: %s() >  Dump Active CCW chain \n",
+               dev->name,__FUNCTION__);
+        p_buf=privptr->p_write_active_first;
+        while (p_buf!=NULL) {
+                dumpit((char *)p_buf, sizeof(struct ccwbk));
+                p_buf=p_buf->next;
+        }
+        p_buf=(struct ccwbk*)privptr->p_end_ccw;
+        dumpit((char *)p_buf, sizeof(struct endccw));
+#endif
+        dev_kfree_skb(skb);
+       if (linkid==0) {
+               lock=LOCK_NO;
+        }
+        else  {
+                lock=LOCK_YES;
+        }
+        claw_strt_out_IO(dev );
+        /*      if write free count is zero , set NOBUFFER       */
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: %s() > free_count is %d\n",
+               dev->name,__FUNCTION__,
+               (int) privptr->write_free_count );
+#endif
+       if (privptr->write_free_count==0) {
+               claw_setbit_busy(TB_NOBUFFER,dev);
+        }
+Done2:
+       claw_clearbit_busy(TB_TX,dev);
+Done:
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s: %s() > exit on line %d, rc = %d \n",
+               dev->name,__FUNCTION__,__LINE__, rc);
+#endif
+       return(rc);
+}    /*    end of claw_hw_tx    */
+
+/*-------------------------------------------------------------------*
+*                                                                    *
+*     init_ccw_bk                                                    *
+*                                                                    *
+*--------------------------------------------------------------------*/
+
+static int
+init_ccw_bk(struct net_device *dev)
+{
+
+        __u32   ccw_blocks_required;
+        __u32   ccw_blocks_perpage;
+        __u32   ccw_pages_required;
+        __u32   claw_reads_perpage=1;
+        __u32   claw_read_pages;
+        __u32   claw_writes_perpage=1;
+        __u32   claw_write_pages;
+        void    *p_buff=NULL;
+        struct ccwbk*p_free_chain;
+       struct ccwbk*p_buf;
+       struct ccwbk*p_last_CCWB;
+       struct ccwbk*p_first_CCWB;
+        struct endccw *p_endccw=NULL;
+        addr_t  real_address;
+        struct claw_privbk *privptr=dev->priv;
+        struct clawh *pClawH=NULL;
+        addr_t   real_TIC_address;
+        int i,j;
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s: %s() enter  \n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"init_ccw");
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: variable dev =\n",dev->name);
+        dumpit((char *) dev, sizeof(struct net_device));
+#endif
+
+        /*  initialize  statistics field */
+        privptr->active_link_ID=0;
+        /*  initialize  ccwbk pointers  */
+        privptr->p_write_free_chain=NULL;   /* pointer to free ccw chain*/
+        privptr->p_write_active_first=NULL; /* pointer to the first write ccw*/
+        privptr->p_write_active_last=NULL;  /* pointer to the last write ccw*/
+        privptr->p_read_active_first=NULL;  /* pointer to the first read ccw*/
+        privptr->p_read_active_last=NULL;   /* pointer to the last read ccw */
+        privptr->p_end_ccw=NULL;            /* pointer to ending ccw        */
+        privptr->p_claw_signal_blk=NULL;    /* pointer to signal block      */
+       privptr->buffs_alloc = 0;
+        memset(&privptr->end_ccw, 0x00, sizeof(struct endccw));
+        memset(&privptr->ctl_bk, 0x00, sizeof(struct clawctl));
+        /*  initialize  free write ccwbk counter  */
+        privptr->write_free_count=0;  /* number of free bufs on write chain */
+        p_last_CCWB = NULL;
+        p_first_CCWB= NULL;
+        /*
+        *  We need 1 CCW block for each read buffer, 1 for each
+        *  write buffer, plus 1 for ClawSignalBlock
+        */
+        ccw_blocks_required =
+               privptr->p_env->read_buffers+privptr->p_env->write_buffers+1;
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: %s() "
+               "ccw_blocks_required=%d\n",
+               dev->name,__FUNCTION__,
+               ccw_blocks_required);
+        printk(KERN_INFO "%s: %s() "
+               "PAGE_SIZE=0x%x\n",
+               dev->name,__FUNCTION__,
+               (unsigned int)PAGE_SIZE);
+        printk(KERN_INFO "%s: %s() > "
+               "PAGE_MASK=0x%x\n",
+               dev->name,__FUNCTION__,
+               (unsigned int)PAGE_MASK);
+#endif
+        /*
+        * compute number of CCW blocks that will fit in a page
+        */
+        ccw_blocks_perpage= PAGE_SIZE /  CCWBK_SIZE;
+        ccw_pages_required=
+               (ccw_blocks_required+ccw_blocks_perpage -1) /
+                        ccw_blocks_perpage;
+
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: %s() > ccw_blocks_perpage=%d\n",
+               dev->name,__FUNCTION__,
+               ccw_blocks_perpage);
+        printk(KERN_INFO "%s: %s() > ccw_pages_required=%d\n",
+               dev->name,__FUNCTION__,
+               ccw_pages_required);
+#endif
+        /*
+         *  read and write sizes are set by 2 constants in claw.h
+        *  4k and 32k.  Unpacked values other than 4k are not going to
+        * provide good performance. With packing buffers support 32k
+        * buffers are used.
+         */
+        if (privptr->p_env->read_size < PAGE_SIZE) {
+            claw_reads_perpage= PAGE_SIZE / privptr->p_env->read_size;
+            claw_read_pages= (privptr->p_env->read_buffers +
+               claw_reads_perpage -1) / claw_reads_perpage;
+         }
+         else {       /* > or equal  */
+            privptr->p_buff_pages_perread=
+               (privptr->p_env->read_size + PAGE_SIZE - 1) / PAGE_SIZE;
+            claw_read_pages=
+               privptr->p_env->read_buffers * privptr->p_buff_pages_perread;
+         }
+        if (privptr->p_env->write_size < PAGE_SIZE) {
+            claw_writes_perpage=
+               PAGE_SIZE / privptr->p_env->write_size;
+            claw_write_pages=
+               (privptr->p_env->write_buffers + claw_writes_perpage -1) /
+                       claw_writes_perpage;
+
+        }
+        else {      /* >  or equal  */
+            privptr->p_buff_pages_perwrite=
+                (privptr->p_env->read_size + PAGE_SIZE - 1) / PAGE_SIZE;
+            claw_write_pages=
+               privptr->p_env->write_buffers * privptr->p_buff_pages_perwrite;
+        }
+#ifdef DEBUGMSG
+        if (privptr->p_env->read_size < PAGE_SIZE) {
+            printk(KERN_INFO "%s: %s() reads_perpage=%d\n",
+               dev->name,__FUNCTION__,
+               claw_reads_perpage);
+        }
+        else {
+            printk(KERN_INFO "%s: %s() pages_perread=%d\n",
+               dev->name,__FUNCTION__,
+               privptr->p_buff_pages_perread);
+        }
+        printk(KERN_INFO "%s: %s() read_pages=%d\n",
+               dev->name,__FUNCTION__,
+               claw_read_pages);
+        if (privptr->p_env->write_size < PAGE_SIZE) {
+            printk(KERN_INFO "%s: %s() writes_perpage=%d\n",
+               dev->name,__FUNCTION__,
+               claw_writes_perpage);
+        }
+        else {
+            printk(KERN_INFO "%s: %s() pages_perwrite=%d\n",
+               dev->name,__FUNCTION__,
+               privptr->p_buff_pages_perwrite);
+        }
+        printk(KERN_INFO "%s: %s() write_pages=%d\n",
+               dev->name,__FUNCTION__,
+               claw_write_pages);
+#endif
+
+
+        /*
+        *               allocate ccw_pages_required
+        */
+        if (privptr->p_buff_ccw==NULL) {
+                privptr->p_buff_ccw=
+                       (void *)__get_free_pages(__GFP_DMA,
+                       (int)pages_to_order_of_mag(ccw_pages_required ));
+                if (privptr->p_buff_ccw==NULL) {
+                        printk(KERN_INFO "%s: %s()  "
+                               "__get_free_pages for CCWs failed : "
+                               "pages is %d\n",
+                                dev->name,__FUNCTION__,
+                               ccw_pages_required );
+#ifdef FUNCTRACE
+                        printk(KERN_INFO "%s: %s() > "
+                               "exit on line %d, rc = ENOMEM\n",
+                               dev->name,__FUNCTION__,
+                                __LINE__);
+#endif
+                        return -ENOMEM;
+                }
+                privptr->p_buff_ccw_num=ccw_pages_required;
+        }
+        memset(privptr->p_buff_ccw, 0x00,
+               privptr->p_buff_ccw_num * PAGE_SIZE);
+
+        /*
+        *               obtain ending ccw block address
+        *
+        */
+        privptr->p_end_ccw = (struct endccw *)&privptr->end_ccw;
+        real_address  = (__u32)__pa(privptr->p_end_ccw);
+        /*                              Initialize ending CCW block       */
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: %s() begin initialize ending CCW blocks\n",
+               dev->name,__FUNCTION__);
+#endif
+
+        p_endccw=privptr->p_end_ccw;
+        p_endccw->real=real_address;
+        p_endccw->write1=0x00;
+        p_endccw->read1=0x00;
+
+        /*      write1_nop1                                     */
+        p_endccw->write1_nop1.cmd_code = CCW_CLAW_CMD_NOP;
+        p_endccw->write1_nop1.flags       = CCW_FLAG_SLI | CCW_FLAG_CC;
+        p_endccw->write1_nop1.count       = 1;
+        p_endccw->write1_nop1.cda         = 0;
+
+        /*      write1_nop2                                     */
+        p_endccw->write1_nop2.cmd_code = CCW_CLAW_CMD_READFF;
+        p_endccw->write1_nop2.flags        = CCW_FLAG_SLI | CCW_FLAG_SKIP;
+        p_endccw->write1_nop2.count      = 1;
+        p_endccw->write1_nop2.cda        = 0;
+
+        /*      write2_nop1                                     */
+        p_endccw->write2_nop1.cmd_code = CCW_CLAW_CMD_NOP;
+        p_endccw->write2_nop1.flags        = CCW_FLAG_SLI | CCW_FLAG_CC;
+        p_endccw->write2_nop1.count        = 1;
+        p_endccw->write2_nop1.cda          = 0;
+
+        /*      write2_nop2                                     */
+        p_endccw->write2_nop2.cmd_code = CCW_CLAW_CMD_READFF;
+        p_endccw->write2_nop2.flags        = CCW_FLAG_SLI | CCW_FLAG_SKIP;
+        p_endccw->write2_nop2.count        = 1;
+        p_endccw->write2_nop2.cda          = 0;
+
+        /*      read1_nop1                                      */
+        p_endccw->read1_nop1.cmd_code = CCW_CLAW_CMD_NOP;
+        p_endccw->read1_nop1.flags        = CCW_FLAG_SLI | CCW_FLAG_CC;
+        p_endccw->read1_nop1.count        = 1;
+        p_endccw->read1_nop1.cda          = 0;
+
+        /*      read1_nop2                                      */
+        p_endccw->read1_nop2.cmd_code = CCW_CLAW_CMD_READFF;
+        p_endccw->read1_nop2.flags        = CCW_FLAG_SLI | CCW_FLAG_SKIP;
+        p_endccw->read1_nop2.count        = 1;
+        p_endccw->read1_nop2.cda          = 0;
+
+        /*      read2_nop1                                      */
+        p_endccw->read2_nop1.cmd_code = CCW_CLAW_CMD_NOP;
+        p_endccw->read2_nop1.flags        = CCW_FLAG_SLI | CCW_FLAG_CC;
+        p_endccw->read2_nop1.count        = 1;
+        p_endccw->read2_nop1.cda          = 0;
+
+        /*      read2_nop2                                      */
+        p_endccw->read2_nop2.cmd_code = CCW_CLAW_CMD_READFF;
+        p_endccw->read2_nop2.flags        = CCW_FLAG_SLI | CCW_FLAG_SKIP;
+        p_endccw->read2_nop2.count        = 1;
+        p_endccw->read2_nop2.cda          = 0;
+
+#ifdef IOTRACE
+        printk(KERN_INFO "%s: %s() dump claw ending CCW BK \n",
+               dev->name,__FUNCTION__);
+        dumpit((char *)p_endccw, sizeof(struct endccw));
+#endif
+
+        /*
+        *                               Build a chain of CCWs
+        *
+        */
+
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: %s()  Begin build a chain of CCW buffer \n",
+               dev->name,__FUNCTION__);
+#endif
+        p_buff=privptr->p_buff_ccw;
+
+        p_free_chain=NULL;
+        for (i=0 ; i < ccw_pages_required; i++ ) {
+                real_address  = (__u32)__pa(p_buff);
+                p_buf=p_buff;
+                for (j=0 ; j < ccw_blocks_perpage ; j++) {
+                        p_buf->next  = p_free_chain;
+                        p_free_chain = p_buf;
+                        p_buf->real=(__u32)__pa(p_buf);
+                        ++p_buf;
+                }
+                p_buff+=PAGE_SIZE;
+        }
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: %s() "
+               "End build a chain of CCW buffer \n",
+                       dev->name,__FUNCTION__);
+        p_buf=p_free_chain;
+        while (p_buf!=NULL) {
+                dumpit((char *)p_buf, sizeof(struct ccwbk));
+                p_buf=p_buf->next;
+        }
+#endif
+
+        /*
+        *                               Initialize ClawSignalBlock
+        *
+        */
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: %s() "
+               "Begin initialize ClawSignalBlock \n",
+               dev->name,__FUNCTION__);
+#endif
+        if (privptr->p_claw_signal_blk==NULL) {
+                privptr->p_claw_signal_blk=p_free_chain;
+                p_free_chain=p_free_chain->next;
+                pClawH=(struct clawh *)privptr->p_claw_signal_blk;
+                pClawH->length=0xffff;
+                pClawH->opcode=0xff;
+                pClawH->flag=CLAW_BUSY;
+        }
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: %s() >  End initialize "
+               "ClawSignalBlock\n",
+               dev->name,__FUNCTION__);
+        dumpit((char *)privptr->p_claw_signal_blk, sizeof(struct ccwbk));
+#endif
+
+        /*
+        *               allocate write_pages_required and add to free chain
+        */
+        if (privptr->p_buff_write==NULL) {
+            if (privptr->p_env->write_size < PAGE_SIZE) {
+                privptr->p_buff_write=
+                       (void *)__get_free_pages(__GFP_DMA,
+                       (int)pages_to_order_of_mag(claw_write_pages ));
+                if (privptr->p_buff_write==NULL) {
+                        printk(KERN_INFO "%s: %s() __get_free_pages for write"
+                               " bufs failed : get is for %d pages\n",
+                                dev->name,__FUNCTION__,claw_write_pages );
+                        free_pages((unsigned long)privptr->p_buff_ccw,
+                          (int)pages_to_order_of_mag(privptr->p_buff_ccw_num));
+                        privptr->p_buff_ccw=NULL;
+#ifdef FUNCTRACE
+                        printk(KERN_INFO "%s: %s() > exit on line %d,"
+                               "rc = ENOMEM\n",
+                               dev->name,__FUNCTION__,__LINE__);
+#endif
+                        return -ENOMEM;
+                }
+                /*
+                *                               Build CLAW write free chain
+                *
+                */
+
+                memset(privptr->p_buff_write, 0x00,
+                       ccw_pages_required * PAGE_SIZE);
+#ifdef DEBUGMSG
+                printk(KERN_INFO "%s: %s() Begin build claw write free "
+                       "chain \n",dev->name,__FUNCTION__);
+#endif
+                privptr->p_write_free_chain=NULL;
+
+                p_buff=privptr->p_buff_write;
+
+                for (i=0 ; i< privptr->p_env->write_buffers ; i++) {
+                        p_buf        = p_free_chain;      /*  get a CCW */
+                        p_free_chain = p_buf->next;
+                        p_buf->next  =privptr->p_write_free_chain;
+                        privptr->p_write_free_chain = p_buf;
+                        p_buf-> p_buffer       = (struct clawbuf *)p_buff;
+                        p_buf-> write.cda       = (__u32)__pa(p_buff);
+                        p_buf-> write.flags     = CCW_FLAG_SLI | CCW_FLAG_CC;
+                        p_buf-> w_read_FF.cmd_code = CCW_CLAW_CMD_READFF;
+                        p_buf-> w_read_FF.flags   = CCW_FLAG_SLI | CCW_FLAG_CC;
+                        p_buf-> w_read_FF.count   = 1;
+                        p_buf-> w_read_FF.cda     =
+                               (__u32)__pa(&p_buf-> header.flag);
+                        p_buf-> w_TIC_1.cmd_code = CCW_CLAW_CMD_TIC;
+                        p_buf-> w_TIC_1.flags      = 0;
+                        p_buf-> w_TIC_1.count      = 0;
+
+                        if (((unsigned long)p_buff+privptr->p_env->write_size) >=
+                          ((unsigned long)(p_buff+2*
+                               (privptr->p_env->write_size) -1) & PAGE_MASK)) {
+                        p_buff= p_buff+privptr->p_env->write_size;
+                        }
+                }
+           }
+           else      /*  Buffers are => PAGE_SIZE. 1 buff per get_free_pages */
+           {
+               privptr->p_write_free_chain=NULL;
+               for (i = 0; i< privptr->p_env->write_buffers ; i++) {
+                   p_buff=(void *)__get_free_pages(__GFP_DMA,
+                       (int)pages_to_order_of_mag(
+                       privptr->p_buff_pages_perwrite) );
+#ifdef IOTRACE
+                   printk(KERN_INFO "%s:%s __get_free_pages "
+                   "for writes buf: get for %d pages\n",
+                   dev->name,__FUNCTION__,
+                   privptr->p_buff_pages_perwrite);
+#endif
+                   if (p_buff==NULL) {
+                        printk(KERN_INFO "%s:%s __get_free_pages"
+                               "for writes buf failed : get is for %d pages\n",
+                               dev->name,
+                               __FUNCTION__,
+                               privptr->p_buff_pages_perwrite );
+                        free_pages((unsigned long)privptr->p_buff_ccw,
+                             (int)pages_to_order_of_mag(
+                                       privptr->p_buff_ccw_num));
+                        privptr->p_buff_ccw=NULL;
+                       p_buf=privptr->p_buff_write;
+                        while (p_buf!=NULL) {
+                                free_pages((unsigned long)
+                                       p_buf->p_buffer,
+                                       (int)pages_to_order_of_mag(
+                                       privptr->p_buff_pages_perwrite));
+                                p_buf=p_buf->next;
+                        }
+#ifdef FUNCTRACE
+                        printk(KERN_INFO "%s: %s exit on line %d, rc = ENOMEM\n",
+                       dev->name,
+                       __FUNCTION__,
+                       __LINE__);
+#endif
+                        return -ENOMEM;
+                   }  /* Error on get_pages   */
+                   memset(p_buff, 0x00, privptr->p_env->write_size );
+                   p_buf         = p_free_chain;
+                   p_free_chain  = p_buf->next;
+                   p_buf->next   = privptr->p_write_free_chain;
+                   privptr->p_write_free_chain = p_buf;
+                   privptr->p_buff_write = p_buf;
+                   p_buf->p_buffer=(struct clawbuf *)p_buff;
+                   p_buf-> write.cda     = (__u32)__pa(p_buff);
+                   p_buf-> write.flags   = CCW_FLAG_SLI | CCW_FLAG_CC;
+                   p_buf-> w_read_FF.cmd_code = CCW_CLAW_CMD_READFF;
+                   p_buf-> w_read_FF.flags    = CCW_FLAG_SLI | CCW_FLAG_CC;
+                   p_buf-> w_read_FF.count    = 1;
+                   p_buf-> w_read_FF.cda      =
+                       (__u32)__pa(&p_buf-> header.flag);
+                   p_buf-> w_TIC_1.cmd_code = CCW_CLAW_CMD_TIC;
+                   p_buf-> w_TIC_1.flags   = 0;
+                   p_buf-> w_TIC_1.count   = 0;
+               }  /* for all write_buffers   */
+
+           }    /* else buffers are PAGE_SIZE or bigger */
+
+        }
+        privptr->p_buff_write_num=claw_write_pages;
+        privptr->write_free_count=privptr->p_env->write_buffers;
+
+
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s:%s  End build claw write free chain \n",
+       dev->name,__FUNCTION__);
+        p_buf=privptr->p_write_free_chain;
+        while (p_buf!=NULL) {
+                dumpit((char *)p_buf, sizeof(struct ccwbk));
+                p_buf=p_buf->next;
+        }
+#endif
+        /*
+        *               allocate read_pages_required and chain to free chain
+        */
+        if (privptr->p_buff_read==NULL) {
+            if (privptr->p_env->read_size < PAGE_SIZE)  {
+                privptr->p_buff_read=
+                       (void *)__get_free_pages(__GFP_DMA,
+                       (int)pages_to_order_of_mag(claw_read_pages) );
+                if (privptr->p_buff_read==NULL) {
+                        printk(KERN_INFO "%s: %s() "
+                               "__get_free_pages for read buf failed : "
+                               "get is for %d pages\n",
+                                dev->name,__FUNCTION__,claw_read_pages );
+                        free_pages((unsigned long)privptr->p_buff_ccw,
+                               (int)pages_to_order_of_mag(
+                                       privptr->p_buff_ccw_num));
+                       /* free the write pages size is < page size  */
+                        free_pages((unsigned long)privptr->p_buff_write,
+                               (int)pages_to_order_of_mag(
+                               privptr->p_buff_write_num));
+                        privptr->p_buff_ccw=NULL;
+                        privptr->p_buff_write=NULL;
+#ifdef FUNCTRACE
+                        printk(KERN_INFO "%s: %s() > exit on line %d, rc ="
+                               " ENOMEM\n",dev->name,__FUNCTION__,__LINE__);
+#endif
+                        return -ENOMEM;
+                }
+                memset(privptr->p_buff_read, 0x00, claw_read_pages * PAGE_SIZE);
+                privptr->p_buff_read_num=claw_read_pages;
+                /*
+                *                               Build CLAW read free chain
+                *
+                */
+#ifdef DEBUGMSG
+                printk(KERN_INFO "%s: %s() Begin build claw read free chain \n",
+                       dev->name,__FUNCTION__);
+#endif
+                p_buff=privptr->p_buff_read;
+                for (i=0 ; i< privptr->p_env->read_buffers ; i++) {
+                        p_buf        = p_free_chain;
+                        p_free_chain = p_buf->next;
+
+                        if (p_last_CCWB==NULL) {
+                                p_buf->next=NULL;
+                                real_TIC_address=0;
+                                p_last_CCWB=p_buf;
+                        }
+                        else {
+                                p_buf->next=p_first_CCWB;
+                                real_TIC_address=
+                               (__u32)__pa(&p_first_CCWB -> read );
+                        }
+
+                        p_first_CCWB=p_buf;
+
+                        p_buf->p_buffer=(struct clawbuf *)p_buff;
+                        /*  initialize read command */
+                        p_buf-> read.cmd_code = CCW_CLAW_CMD_READ;
+                        p_buf-> read.cda = (__u32)__pa(p_buff);
+                        p_buf-> read.flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+                        p_buf-> read.count       = privptr->p_env->read_size;
+
+                        /*  initialize read_h command */
+                        p_buf-> read_h.cmd_code = CCW_CLAW_CMD_READHEADER;
+                        p_buf-> read_h.cda =
+                               (__u32)__pa(&(p_buf->header));
+                        p_buf-> read_h.flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+                        p_buf-> read_h.count      = sizeof(struct clawh);
+
+                        /*  initialize Signal command */
+                        p_buf-> signal.cmd_code = CCW_CLAW_CMD_SIGNAL_SMOD;
+                        p_buf-> signal.cda =
+                               (__u32)__pa(&(pClawH->flag));
+                        p_buf-> signal.flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+                        p_buf-> signal.count     = 1;
+
+                        /*  initialize r_TIC_1 command */
+                        p_buf-> r_TIC_1.cmd_code = CCW_CLAW_CMD_TIC;
+                        p_buf-> r_TIC_1.cda = (__u32)real_TIC_address;
+                        p_buf-> r_TIC_1.flags = 0;
+                        p_buf-> r_TIC_1.count      = 0;
+
+                        /*  initialize r_read_FF command */
+                        p_buf-> r_read_FF.cmd_code = CCW_CLAW_CMD_READFF;
+                        p_buf-> r_read_FF.cda =
+                               (__u32)__pa(&(pClawH->flag));
+                        p_buf-> r_read_FF.flags =
+                               CCW_FLAG_SLI | CCW_FLAG_CC | CCW_FLAG_PCI;
+                        p_buf-> r_read_FF.count    = 1;
+
+                        /*    initialize r_TIC_2          */
+                        memcpy(&p_buf->r_TIC_2,
+                               &p_buf->r_TIC_1, sizeof(struct ccw1));
+
+                        /*     initialize Header     */
+                        p_buf->header.length=0xffff;
+                        p_buf->header.opcode=0xff;
+                        p_buf->header.flag=CLAW_PENDING;
+
+                        if (((unsigned long)p_buff+privptr->p_env->read_size) >=
+                               ((unsigned long)(p_buff+2*(privptr->p_env->read_size) -1)
+                                & PAGE_MASK) ) {
+                                p_buff= p_buff+privptr->p_env->read_size;
+                        }
+                        else {
+                                p_buff=
+                               (void *)((unsigned long)
+                                       (p_buff+2*(privptr->p_env->read_size) -1)
+                                        & PAGE_MASK) ;
+                        }
+                }   /* for read_buffers   */
+          }         /* read_size < PAGE_SIZE  */
+          else {  /* read Size >= PAGE_SIZE  */
+
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: %s() Begin build claw read free chain \n",
+               dev->name,__FUNCTION__);
+#endif
+                for (i=0 ; i< privptr->p_env->read_buffers ; i++) {
+                        p_buff = (void *)__get_free_pages(__GFP_DMA,
+                               (int)pages_to_order_of_mag(privptr->p_buff_pages_perread) );
+                        if (p_buff==NULL) {
+                                printk(KERN_INFO "%s: %s() __get_free_pages for read "
+                                       "buf failed : get is for %d pages\n",
+                                       dev->name,__FUNCTION__,
+                                        privptr->p_buff_pages_perread );
+                                free_pages((unsigned long)privptr->p_buff_ccw,
+                                       (int)pages_to_order_of_mag(privptr->p_buff_ccw_num));
+                               /* free the write pages  */
+                               p_buf=privptr->p_buff_write;
+                                while (p_buf!=NULL) {
+                                        free_pages((unsigned long)p_buf->p_buffer,
+                                               (int)pages_to_order_of_mag(
+                                               privptr->p_buff_pages_perwrite ));
+                                        p_buf=p_buf->next;
+                                }
+                               /* free any read pages already alloc  */
+                               p_buf=privptr->p_buff_read;
+                                while (p_buf!=NULL) {
+                                        free_pages((unsigned long)p_buf->p_buffer,
+                                               (int)pages_to_order_of_mag(
+                                               privptr->p_buff_pages_perread ));
+                                        p_buf=p_buf->next;
+                                }
+                                privptr->p_buff_ccw=NULL;
+                                privptr->p_buff_write=NULL;
+#ifdef FUNCTRACE
+                                printk(KERN_INFO "%s: %s() exit on line %d, rc = ENOMEM\n",
+                                       dev->name,__FUNCTION__,
+                                       __LINE__);
+#endif
+                                return -ENOMEM;
+                        }
+                        memset(p_buff, 0x00, privptr->p_env->read_size);
+                        p_buf        = p_free_chain;
+                        privptr->p_buff_read = p_buf;
+                        p_free_chain = p_buf->next;
+
+                        if (p_last_CCWB==NULL) {
+                                p_buf->next=NULL;
+                                real_TIC_address=0;
+                                p_last_CCWB=p_buf;
+                        }
+                        else {
+                                p_buf->next=p_first_CCWB;
+                                real_TIC_address=
+                                       (addr_t)__pa(
+                                               &p_first_CCWB -> read );
+                        }
+
+                        p_first_CCWB=p_buf;
+                               /* save buff address */
+                        p_buf->p_buffer=(struct clawbuf *)p_buff;
+                        /*  initialize read command */
+                        p_buf-> read.cmd_code = CCW_CLAW_CMD_READ;
+                        p_buf-> read.cda = (__u32)__pa(p_buff);
+                        p_buf-> read.flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+                        p_buf-> read.count       = privptr->p_env->read_size;
+
+                        /*  initialize read_h command */
+                        p_buf-> read_h.cmd_code = CCW_CLAW_CMD_READHEADER;
+                        p_buf-> read_h.cda =
+                               (__u32)__pa(&(p_buf->header));
+                        p_buf-> read_h.flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+                        p_buf-> read_h.count      = sizeof(struct clawh);
+
+                        /*  initialize Signal command */
+                        p_buf-> signal.cmd_code = CCW_CLAW_CMD_SIGNAL_SMOD;
+                        p_buf-> signal.cda =
+                               (__u32)__pa(&(pClawH->flag));
+                        p_buf-> signal.flags = CCW_FLAG_SLI | CCW_FLAG_CC;
+                        p_buf-> signal.count     = 1;
+
+                        /*  initialize r_TIC_1 command */
+                        p_buf-> r_TIC_1.cmd_code = CCW_CLAW_CMD_TIC;
+                        p_buf-> r_TIC_1.cda = (__u32)real_TIC_address;
+                        p_buf-> r_TIC_1.flags = 0;
+                        p_buf-> r_TIC_1.count      = 0;
+
+                        /*  initialize r_read_FF command */
+                        p_buf-> r_read_FF.cmd_code = CCW_CLAW_CMD_READFF;
+                        p_buf-> r_read_FF.cda =
+                               (__u32)__pa(&(pClawH->flag));
+                        p_buf-> r_read_FF.flags =
+                               CCW_FLAG_SLI | CCW_FLAG_CC | CCW_FLAG_PCI;
+                        p_buf-> r_read_FF.count    = 1;
+
+                        /*    initialize r_TIC_2          */
+                        memcpy(&p_buf->r_TIC_2, &p_buf->r_TIC_1,
+                               sizeof(struct ccw1));
+
+                        /*     initialize Header     */
+                        p_buf->header.length=0xffff;
+                        p_buf->header.opcode=0xff;
+                        p_buf->header.flag=CLAW_PENDING;
+
+                }    /* For read_buffers   */
+          }     /*  read_size >= PAGE_SIZE   */
+        }       /*  pBuffread = NULL */
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: %s() >  End build claw read free chain \n",
+               dev->name,__FUNCTION__);
+        p_buf=p_first_CCWB;
+        while (p_buf!=NULL) {
+                dumpit((char *)p_buf, sizeof(struct ccwbk));
+                p_buf=p_buf->next;
+        }
+
+#endif
+        add_claw_reads( dev  ,p_first_CCWB , p_last_CCWB);
+       privptr->buffs_alloc = 1;
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s: %s() exit on line %d\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+        return 0;
+}    /*    end of init_ccw_bk */
+
+/*-------------------------------------------------------------------*
+*                                                                    *
+*       probe_error                                                  *
+*                                                                    *
+*--------------------------------------------------------------------*/
+
+static void
+probe_error( struct ccwgroup_device *cgdev)
+{
+  struct claw_privbk *privptr;
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s enter  \n",__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"proberr");
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s variable cgdev =\n",__FUNCTION__);
+        dumpit((char *) cgdev, sizeof(struct ccwgroup_device));
+#endif
+        privptr=(struct claw_privbk *)cgdev->dev.driver_data;
+       if (privptr!=NULL) {
+               if (privptr->p_env != NULL) {
+                       kfree(privptr->p_env);
+                       privptr->p_env=NULL;
+               }
+               if (privptr->p_mtc_envelope!=NULL) {
+                       kfree(privptr->p_mtc_envelope);
+                       privptr->p_mtc_envelope=NULL;
+               }
+                kfree(privptr);
+                privptr=NULL;
+        }
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s > exit on line %d\n",
+                __FUNCTION__,__LINE__);
+#endif
+
+        return;
+}    /*    probe_error    */
+
+
+
+/*-------------------------------------------------------------------*
+*    claw_process_control                                            *
+*                                                                    *
+*                                                                    *
+*--------------------------------------------------------------------*/
+
+static int
+claw_process_control( struct net_device *dev, struct ccwbk * p_ccw)
+{
+
+        struct clawbuf *p_buf;
+        struct clawctl  ctlbk;
+        struct clawctl *p_ctlbk;
+        char    temp_host_name[8];
+        char    temp_ws_name[8];
+        struct claw_privbk *privptr;
+        struct claw_env *p_env;
+        struct sysval *p_sysval;
+        struct conncmd *p_connect=NULL;
+        int rc;
+        struct chbk *p_ch = NULL;
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s: %s() > enter  \n",
+               dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(2,setup,"clw_cntl");
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: variable dev =\n",dev->name);
+        dumpit((char *) dev, sizeof(struct net_device));
+        printk(KERN_INFO "%s: variable p_ccw =\n",dev->name);
+        dumpit((char *) p_ccw, sizeof(struct ccwbk *));
+#endif
+        udelay(1000);  /* Wait a ms for the control packets to
+                       *catch up to each other */
+        privptr=dev->priv;
+        p_env=privptr->p_env;
+       memcpy( &temp_host_name, p_env->host_name, 8);
+        memcpy( &temp_ws_name, p_env->adapter_name , 8);
+        printk(KERN_INFO "%s: CLAW device %.8s: "
+               "Received Control Packet\n",
+               dev->name, temp_ws_name);
+        if (privptr->release_pend==1) {
+#ifdef FUNCTRACE
+                printk(KERN_INFO "%s: %s() > "
+                       "exit on line %d, rc=0\n",
+                       dev->name,__FUNCTION__,__LINE__);
+#endif
+                return 0;
+        }
+        p_buf=p_ccw->p_buffer;
+        p_ctlbk=&ctlbk;
+       if (p_env->packing == DO_PACKED) { /* packing in progress?*/
+               memcpy(p_ctlbk, &p_buf->buffer[4], sizeof(struct clawctl));
+       } else {
+               memcpy(p_ctlbk, p_buf, sizeof(struct clawctl));
+       }
+#ifdef IOTRACE
+        printk(KERN_INFO "%s: dump claw control data inbound\n",dev->name);
+        dumpit((char *)p_ctlbk, sizeof(struct clawctl));
+#endif
+        switch (p_ctlbk->command)
+        {
+                case SYSTEM_VALIDATE_REQUEST:
+                        if (p_ctlbk->version!=CLAW_VERSION_ID) {
+                                claw_snd_sys_validate_rsp(dev, p_ctlbk,
+                                       CLAW_RC_WRONG_VERSION );
+                                printk("%s: %d is wrong version id. "
+                                       "Expected %d\n",
+                                       dev->name, p_ctlbk->version,
+                                        CLAW_VERSION_ID);
+                        }
+                        p_sysval=(struct sysval *)&(p_ctlbk->data);
+                       printk( "%s: Recv Sys Validate Request: "
+                               "Vers=%d,link_id=%d,Corr=%d,WS name=%."
+                               "8s,Host name=%.8s\n",
+                                dev->name, p_ctlbk->version,
+                               p_ctlbk->linkid,
+                               p_ctlbk->correlator,
+                               p_sysval->WS_name,
+                                p_sysval->host_name);
+                        if (0!=memcmp(temp_host_name,p_sysval->host_name,8)) {
+                                claw_snd_sys_validate_rsp(dev, p_ctlbk,
+                                       CLAW_RC_NAME_MISMATCH );
+                               CLAW_DBF_TEXT(2,setup,"HSTBAD");
+                               CLAW_DBF_TEXT_(2,setup,"%s",p_sysval->host_name);
+                               CLAW_DBF_TEXT_(2,setup,"%s",temp_host_name);
+                                printk(KERN_INFO "%s:  Host name mismatch\n",
+                                       dev->name);
+                               printk(KERN_INFO "%s: Received :%s: "
+                                       "expected :%s: \n",
+                                       dev->name,
+                                       p_sysval->host_name,
+                                       temp_host_name);
+                        }
+                        if (0!=memcmp(temp_ws_name,p_sysval->WS_name,8)) {
+                                claw_snd_sys_validate_rsp(dev, p_ctlbk,
+                                       CLAW_RC_NAME_MISMATCH );
+                               CLAW_DBF_TEXT(2,setup,"WSNBAD");
+                                CLAW_DBF_TEXT_(2,setup,"%s",p_sysval->WS_name);
+                                CLAW_DBF_TEXT_(2,setup,"%s",temp_ws_name);
+                                printk(KERN_INFO "%s: WS name mismatch\n",
+                                       dev->name);
+                                printk(KERN_INFO "%s: Received :%s: "
+                                        "expected :%s: \n",
+                                        dev->name,
+                                        p_sysval->WS_name,
+                                       temp_ws_name);
+                        }
+                        if (( p_sysval->write_frame_size < p_env->write_size) &&
+                          ( p_env->packing == 0)) {
+                                claw_snd_sys_validate_rsp(dev, p_ctlbk,
+                                       CLAW_RC_HOST_RCV_TOO_SMALL );
+                                printk(KERN_INFO "%s: host write size is too "
+                                       "small\n", dev->name);
+                               CLAW_DBF_TEXT(2,setup,"wrtszbad");
+                        }
+                        if (( p_sysval->read_frame_size < p_env->read_size) &&
+                          ( p_env->packing == 0)) {
+                                claw_snd_sys_validate_rsp(dev, p_ctlbk,
+                                       CLAW_RC_HOST_RCV_TOO_SMALL );
+                                printk(KERN_INFO "%s: host read size is too "
+                                       "small\n", dev->name);
+                               CLAW_DBF_TEXT(2,setup,"rdsizbad");
+                        }
+                        claw_snd_sys_validate_rsp(dev, p_ctlbk, 0 );
+                        printk("%s: CLAW device %.8s: System validate"
+                               " completed.\n",dev->name, temp_ws_name);
+                       printk("%s: sys Validate Rsize:%d Wsize:%d\n",dev->name,
+                               p_sysval->read_frame_size,p_sysval->write_frame_size);
+                        privptr->system_validate_comp=1;
+                       if(strncmp(p_env->api_type,WS_APPL_NAME_PACKED,6) == 0) {
+                               p_env->packing = PACKING_ASK;
+                       }
+                        claw_strt_conn_req(dev);
+                        break;
+
+                case SYSTEM_VALIDATE_RESPONSE:
+                       p_sysval=(struct sysval *)&(p_ctlbk->data);
+                       printk("%s: Recv Sys Validate Resp: Vers=%d,Corr=%d,RC=%d,"
+                               "WS name=%.8s,Host name=%.8s\n",
+                               dev->name,
+                               p_ctlbk->version,
+                               p_ctlbk->correlator,
+                               p_ctlbk->rc,
+                               p_sysval->WS_name,
+                               p_sysval->host_name);
+                        switch (p_ctlbk->rc)
+                        {
+                                case 0:
+                                        printk(KERN_INFO "%s: CLAW device "
+                                               "%.8s: System validate "
+                                               "completed.\n",
+                                                dev->name, temp_ws_name);
+                                       if (privptr->system_validate_comp == 0)
+                                               claw_strt_conn_req(dev);
+                                       privptr->system_validate_comp=1;
+                                        break;
+                                case CLAW_RC_NAME_MISMATCH:
+                                        printk(KERN_INFO "%s: Sys Validate "
+                                               "Resp : Host, WS name is "
+                                               "mismatch\n",
+                                                dev->name);
+                                        break;
+                                case CLAW_RC_WRONG_VERSION:
+                                        printk(KERN_INFO "%s: Sys Validate "
+                                               "Resp : Wrong version\n",
+                                               dev->name);
+                                        break;
+                                case CLAW_RC_HOST_RCV_TOO_SMALL:
+                                        printk(KERN_INFO "%s: Sys Validate "
+                                               "Resp : bad frame size\n",
+                                               dev->name);
+                                        break;
+                                default:
+                                        printk(KERN_INFO "%s: Sys Validate "
+                                               "error code=%d \n",
+                                                dev->name, p_ctlbk->rc );
+                                        break;
+                        }
+                        break;
+
+                case CONNECTION_REQUEST:
+                        p_connect=(struct conncmd *)&(p_ctlbk->data);
+                        printk(KERN_INFO "%s: Recv Conn Req: Vers=%d,link_id=%d,"
+                               "Corr=%d,HOST appl=%.8s,WS appl=%.8s\n",
+                               dev->name,
+                               p_ctlbk->version,
+                               p_ctlbk->linkid,
+                               p_ctlbk->correlator,
+                               p_connect->host_name,
+                               p_connect->WS_name);
+                        if (privptr->active_link_ID!=0 ) {
+                                claw_snd_disc(dev, p_ctlbk);
+                                printk(KERN_INFO "%s: Conn Req error : "
+                                       "already logical link is active \n",
+                                       dev->name);
+                        }
+                        if (p_ctlbk->linkid!=1 ) {
+                                claw_snd_disc(dev, p_ctlbk);
+                                printk(KERN_INFO "%s: Conn Req error : "
+                                       "req logical link id is not 1\n",
+                                       dev->name);
+                        }
+                        rc=find_link(dev,
+                               p_connect->host_name, p_connect->WS_name);
+                        if (rc!=0) {
+                                claw_snd_disc(dev, p_ctlbk);
+                                printk(KERN_INFO "%s: Conn Req error : "
+                                       "req appl name does not match\n",
+                                        dev->name);
+                        }
+                        claw_send_control(dev,
+                               CONNECTION_CONFIRM, p_ctlbk->linkid,
+                               p_ctlbk->correlator,
+                               0, p_connect->host_name,
+                                p_connect->WS_name);
+                       if (p_env->packing == PACKING_ASK) {
+                               printk("%s: Now Pack ask\n",dev->name);
+                               p_env->packing = PACK_SEND;
+                               claw_snd_conn_req(dev,0);
+                       }
+                        printk(KERN_INFO "%s: CLAW device %.8s: Connection "
+                               "completed link_id=%d.\n",
+                               dev->name, temp_ws_name,
+                                p_ctlbk->linkid);
+                        privptr->active_link_ID=p_ctlbk->linkid;
+                        p_ch=&privptr->channel[WRITE];
+                        wake_up(&p_ch->wait);  /* wake up claw_open ( WRITE) */
+                        break;
+                case CONNECTION_RESPONSE:
+                        p_connect=(struct conncmd *)&(p_ctlbk->data);
+                        printk(KERN_INFO "%s: Revc Conn Resp: Vers=%d,link_id=%d,"
+                               "Corr=%d,RC=%d,Host appl=%.8s, WS appl=%.8s\n",
+                                dev->name,
+                               p_ctlbk->version,
+                               p_ctlbk->linkid,
+                               p_ctlbk->correlator,
+                               p_ctlbk->rc,
+                               p_connect->host_name,
+                                p_connect->WS_name);
+
+                        if (p_ctlbk->rc !=0 ) {
+                                printk(KERN_INFO "%s: Conn Resp error: rc=%d \n",
+                                       dev->name, p_ctlbk->rc);
+                                return 1;
+                        }
+                        rc=find_link(dev,
+                               p_connect->host_name, p_connect->WS_name);
+                        if (rc!=0) {
+                                claw_snd_disc(dev, p_ctlbk);
+                                printk(KERN_INFO "%s: Conn Resp error: "
+                                       "req appl name does not match\n",
+                                        dev->name);
+                        }
+                       /* should be until CONNECTION_CONFIRM */
+                        privptr->active_link_ID =  - (p_ctlbk->linkid);
+                        break;
+                case CONNECTION_CONFIRM:
+                        p_connect=(struct conncmd *)&(p_ctlbk->data);
+                        printk(KERN_INFO "%s: Recv Conn Confirm:Vers=%d,link_id=%d,"
+                               "Corr=%d,Host appl=%.8s,WS appl=%.8s\n",
+                        dev->name,
+                        p_ctlbk->version,
+                        p_ctlbk->linkid,
+                        p_ctlbk->correlator,
+                        p_connect->host_name,
+                        p_connect->WS_name);
+                        if (p_ctlbk->linkid== -(privptr->active_link_ID)) {
+                                privptr->active_link_ID=p_ctlbk->linkid;
+                               if (p_env->packing > PACKING_ASK) {
+                                       printk(KERN_INFO "%s: Confirmed Now packing\n",dev->name);
+                                       p_env->packing = DO_PACKED;
+                                       }
+                               p_ch=&privptr->channel[WRITE];
+                                wake_up(&p_ch->wait);
+                        }
+                        else {
+                                printk(KERN_INFO "%s: Conn confirm: "
+                                       "unexpected linkid=%d \n",
+                                       dev->name, p_ctlbk->linkid);
+                                claw_snd_disc(dev, p_ctlbk);
+                        }
+                        break;
+                case DISCONNECT:
+                        printk(KERN_INFO "%s: Disconnect: "
+                               "Vers=%d,link_id=%d,Corr=%d\n",
+                               dev->name, p_ctlbk->version,
+                                p_ctlbk->linkid, p_ctlbk->correlator);
+                       if ((p_ctlbk->linkid == 2) &&
+                           (p_env->packing == PACK_SEND)) {
+                               privptr->active_link_ID = 1;
+                               p_env->packing = DO_PACKED;
+                       }
+                       else
+                               privptr->active_link_ID=0;
+                        break;
+                case CLAW_ERROR:
+                        printk(KERN_INFO "%s: CLAW ERROR detected\n",
+                               dev->name);
+                        break;
+                default:
+                        printk(KERN_INFO "%s:  Unexpected command code=%d \n",
+                               dev->name,  p_ctlbk->command);
+                        break;
+        }
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s: %s() exit on line %d, rc = 0\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+
+        return 0;
+}   /*    end of claw_process_control    */
+
+
+/*-------------------------------------------------------------------*
+*               claw_send_control                                    *
+*                                                                    *
+*--------------------------------------------------------------------*/
+
+static int
+claw_send_control(struct net_device *dev, __u8 type, __u8 link,
+        __u8 correlator, __u8 rc, char *local_name, char *remote_name)
+{
+        struct claw_privbk             *privptr;
+        struct clawctl                  *p_ctl;
+        struct sysval                   *p_sysval;
+        struct conncmd                  *p_connect;
+        struct sk_buff                         *skb;
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s > enter  \n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(2,setup,"sndcntl");
+#ifdef DEBUGMSG
+       printk(KERN_INFO "%s: Sending Control Packet \n",dev->name);
+        printk(KERN_INFO "%s: variable type = 0x%X, link = "
+               "%d, correlator = %d, rc = %d\n",
+                dev->name,type, link, correlator, rc);
+        printk(KERN_INFO "%s: variable local_name = %s, "
+               "remote_name = %s\n",dev->name, local_name, remote_name);
+#endif
+        privptr=dev->priv;
+        p_ctl=(struct clawctl *)&privptr->ctl_bk;
+
+        p_ctl->command=type;
+        p_ctl->version=CLAW_VERSION_ID;
+        p_ctl->linkid=link;
+        p_ctl->correlator=correlator;
+        p_ctl->rc=rc;
+
+        p_sysval=(struct sysval *)&p_ctl->data;
+        p_connect=(struct conncmd *)&p_ctl->data;
+
+        switch (p_ctl->command) {
+                case SYSTEM_VALIDATE_REQUEST:
+                case SYSTEM_VALIDATE_RESPONSE:
+                        memcpy(&p_sysval->host_name, local_name, 8);
+                        memcpy(&p_sysval->WS_name, remote_name, 8);
+                       if (privptr->p_env->packing > 0) {
+                               p_sysval->read_frame_size=DEF_PACK_BUFSIZE;
+                               p_sysval->write_frame_size=DEF_PACK_BUFSIZE;
+                       } else {
+                               /* how big is the piggest group of packets */
+                               p_sysval->read_frame_size=privptr->p_env->read_size;
+                               p_sysval->write_frame_size=privptr->p_env->write_size;
+                       }
+                        memset(&p_sysval->reserved, 0x00, 4);
+                        break;
+                case CONNECTION_REQUEST:
+                case CONNECTION_RESPONSE:
+                case CONNECTION_CONFIRM:
+                case DISCONNECT:
+                        memcpy(&p_sysval->host_name, local_name, 8);
+                        memcpy(&p_sysval->WS_name, remote_name, 8);
+                       if (privptr->p_env->packing > 0) {
+                       /* How big is the biggest packet */
+                               p_connect->reserved1[0]=CLAW_FRAME_SIZE;
+                               p_connect->reserved1[1]=CLAW_FRAME_SIZE;
+                       } else {
+                               memset(&p_connect->reserved1, 0x00, 4);
+                               memset(&p_connect->reserved2, 0x00, 4);
+                       }
+                        break;
+                default:
+                        break;
+        }
+
+        /*      write Control Record to the device                   */
+
+
+        skb = dev_alloc_skb(sizeof(struct clawctl));
+        if (!skb) {
+                printk(  "%s:%s low on mem, returning...\n",
+                       dev->name,__FUNCTION__);
+#ifdef DEBUG
+                printk(KERN_INFO "%s:%s Exit, rc = ENOMEM\n",
+                       dev->name,__FUNCTION__);
+#endif
+                return -ENOMEM;
+        }
+       memcpy(skb_put(skb, sizeof(struct clawctl)),
+               p_ctl, sizeof(struct clawctl));
+#ifdef IOTRACE
+        printk(KERN_INFO "%s: outbnd claw cntl data \n",dev->name);
+        dumpit((char *)p_ctl,sizeof(struct clawctl));
+#endif
+       if (privptr->p_env->packing >= PACK_SEND)
+               claw_hw_tx(skb, dev, 1);
+       else
+               claw_hw_tx(skb, dev, 0);
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Exit on line %d\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+
+        return 0;
+}  /*   end of claw_send_control  */
+
+/*-------------------------------------------------------------------*
+*               claw_snd_conn_req                                    *
+*                                                                    *
+*--------------------------------------------------------------------*/
+static int
+claw_snd_conn_req(struct net_device *dev, __u8 link)
+{
+        int                rc;
+        struct claw_privbk *privptr=dev->priv;
+        struct clawctl            *p_ctl;
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter  \n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(2,setup,"snd_conn");
+#ifdef  DEBUGMSG
+        printk(KERN_INFO "%s: variable link = %X, dev =\n",dev->name, link);
+        dumpit((char *) dev, sizeof(struct net_device));
+#endif
+       rc = 1;
+        p_ctl=(struct clawctl *)&privptr->ctl_bk;
+       p_ctl->linkid = link;
+        if ( privptr->system_validate_comp==0x00 ) {
+#ifdef FUNCTRACE
+                printk(KERN_INFO "%s:%s Exit on line %d, rc = 1\n",
+                       dev->name,__FUNCTION__,__LINE__);
+#endif
+                return rc;
+        }
+       if (privptr->p_env->packing == PACKING_ASK )
+               rc=claw_send_control(dev, CONNECTION_REQUEST,0,0,0,
+                       WS_APPL_NAME_PACKED, WS_APPL_NAME_PACKED);
+       if (privptr->p_env->packing == PACK_SEND)  {
+               rc=claw_send_control(dev, CONNECTION_REQUEST,0,0,0,
+                       WS_APPL_NAME_IP_NAME, WS_APPL_NAME_IP_NAME);
+       }
+       if (privptr->p_env->packing == 0)
+               rc=claw_send_control(dev, CONNECTION_REQUEST,0,0,0,
+                               HOST_APPL_NAME, privptr->p_env->api_type);
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n",
+               dev->name,__FUNCTION__,__LINE__, rc);
+#endif
+        return rc;
+
+}  /*  end of claw_snd_conn_req */
+
+
+/*-------------------------------------------------------------------*
+*               claw_snd_disc                                        *
+*                                                                    *
+*--------------------------------------------------------------------*/
+
+static int
+claw_snd_disc(struct net_device *dev, struct clawctl * p_ctl)
+{
+        int rc;
+        struct conncmd *  p_connect;
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(2,setup,"snd_dsc");
+#ifdef  DEBUGMSG
+        printk(KERN_INFO "%s: variable dev =\n",dev->name);
+        dumpit((char *) dev, sizeof(struct net_device));
+        printk(KERN_INFO "%s: variable p_ctl",dev->name);
+        dumpit((char *) p_ctl, sizeof(struct clawctl));
+#endif
+        p_connect=(struct conncmd *)&p_ctl->data;
+
+        rc=claw_send_control(dev, DISCONNECT, p_ctl->linkid,
+               p_ctl->correlator, 0,
+                p_connect->host_name, p_connect->WS_name);
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n",
+               dev->name,__FUNCTION__, __LINE__, rc);
+#endif
+        return rc;
+}     /*   end of claw_snd_disc    */
+
+
+/*-------------------------------------------------------------------*
+*               claw_snd_sys_validate_rsp                            *
+*                                                                    *
+*--------------------------------------------------------------------*/
+
+static int
+claw_snd_sys_validate_rsp(struct net_device *dev,
+       struct clawctl *p_ctl, __u32 return_code)
+{
+        struct claw_env *  p_env;
+        struct claw_privbk *privptr;
+        int    rc;
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter\n",
+               dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(2,setup,"chkresp");
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: variable return_code = %d, dev =\n",
+               dev->name, return_code);
+        dumpit((char *) dev, sizeof(struct net_device));
+        printk(KERN_INFO "%s: variable p_ctl =\n",dev->name);
+        dumpit((char *) p_ctl, sizeof(struct clawctl));
+#endif
+        privptr = dev->priv;
+        p_env=privptr->p_env;
+        rc=claw_send_control(dev, SYSTEM_VALIDATE_RESPONSE,
+               p_ctl->linkid,
+               p_ctl->correlator,
+                return_code,
+               p_env->host_name,
+               p_env->adapter_name  );
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n",
+               dev->name,__FUNCTION__,__LINE__, rc);
+#endif
+        return rc;
+}     /*    end of claw_snd_sys_validate_rsp    */
+
+/*-------------------------------------------------------------------*
+*               claw_strt_conn_req                                   *
+*                                                                    *
+*--------------------------------------------------------------------*/
+
+static int
+claw_strt_conn_req(struct net_device *dev )
+{
+        int rc;
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(2,setup,"conn_req");
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s: variable dev =\n",dev->name);
+        dumpit((char *) dev, sizeof(struct net_device));
+#endif
+        rc=claw_snd_conn_req(dev, 1);
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Exit on line %d, rc = %d\n",
+               dev->name,__FUNCTION__,__LINE__, rc);
+#endif
+        return rc;
+}    /*   end of claw_strt_conn_req   */
+
+
+
+/*-------------------------------------------------------------------*
+ *   claw_stats                                                      *
+ *-------------------------------------------------------------------*/
+
+static struct
+net_device_stats *claw_stats(struct net_device *dev)
+{
+        struct claw_privbk *privptr;
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"stats");
+        privptr = dev->priv;
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Exit on line %d\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+        return &privptr->stats;
+}     /*   end of claw_stats   */
+
+
+/*-------------------------------------------------------------------*
+*       unpack_read                                                  *
+*                                                                    *
+*--------------------------------------------------------------------*/
+static void
+unpack_read(struct net_device *dev )
+{
+        struct sk_buff *skb;
+        struct claw_privbk *privptr;
+       struct claw_env    *p_env;
+        struct ccwbk   *p_this_ccw;
+        struct ccwbk   *p_first_ccw;
+        struct ccwbk   *p_last_ccw;
+       struct clawph   *p_packh;
+       void            *p_packd;
+       struct clawctl  *p_ctlrec=NULL;
+
+        __u32  len_of_data;
+       __u32   pack_off;
+        __u8   link_num;
+        __u8   mtc_this_frm=0;
+        __u32  bytes_to_mov;
+        struct chbk *p_ch = NULL;
+        int    i=0;
+       int     p=0;
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s enter  \n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"unpkread");
+        p_first_ccw=NULL;
+        p_last_ccw=NULL;
+       p_packh=NULL;
+       p_packd=NULL;
+        privptr=dev->priv;
+       p_env = privptr->p_env;
+        p_this_ccw=privptr->p_read_active_first;
+        i=0;
+       while (p_this_ccw!=NULL && p_this_ccw->header.flag!=CLAW_PENDING) {
+#ifdef IOTRACE
+               printk(KERN_INFO "%s p_this_ccw \n",dev->name);
+                dumpit((char*)p_this_ccw, sizeof(struct ccwbk));
+                printk(KERN_INFO "%s Inbound p_this_ccw->p_buffer(64)"
+                       " pk=%d \n",dev->name,p_env->packing);
+                dumpit((char *)p_this_ccw->p_buffer, 64 );
+#endif
+               pack_off = 0;
+               p = 0;
+               p_this_ccw->header.flag=CLAW_PENDING;
+               privptr->p_read_active_first=p_this_ccw->next;
+                p_this_ccw->next=NULL;
+               p_packh = (struct clawph *)p_this_ccw->p_buffer;
+               if ((p_env->packing == PACK_SEND) &&
+                   (p_packh->len == 32)           &&
+                   (p_packh->link_num == 0)) {   /* is it a packed ctl rec? */
+                       p_packh++;  /* peek past pack header */
+                       p_ctlrec = (struct clawctl *)p_packh;
+                       p_packh--;  /* un peek */
+                       if ((p_ctlrec->command == CONNECTION_RESPONSE) ||
+                           (p_ctlrec->command == CONNECTION_CONFIRM))
+                               p_env->packing = DO_PACKED;
+               }
+               if (p_env->packing == DO_PACKED)
+                       link_num=p_packh->link_num;
+               else
+                       link_num=p_this_ccw->header.opcode / 8;
+                if ((p_this_ccw->header.opcode & MORE_to_COME_FLAG)!=0) {
+#ifdef DEBUGMSG
+                        printk(KERN_INFO "%s: %s > More_to_come is ON\n",
+                       dev->name,__FUNCTION__);
+#endif
+                        mtc_this_frm=1;
+                        if (p_this_ccw->header.length!=
+                               privptr->p_env->read_size ) {
+                                printk(KERN_INFO " %s: Invalid frame detected "
+                                       "length is %02x\n" ,
+                                        dev->name, p_this_ccw->header.length);
+                        }
+                }
+
+                if (privptr->mtc_skipping) {
+                        /*
+                        *   We're in the mode of skipping past a
+                       *   multi-frame message
+                        *   that we can't process for some reason or other.
+                        *   The first frame without the More-To-Come flag is
+                       *   the last frame of the skipped message.
+                        */
+                        /*  in case of More-To-Come not set in this frame */
+                        if (mtc_this_frm==0) {
+                                privptr->mtc_skipping=0; /* Ok, the end */
+                                privptr->mtc_logical_link=-1;
+                        }
+#ifdef DEBUGMSG
+                        printk(KERN_INFO "%s:%s goto next "
+                               "frame from MoretoComeSkip \n",
+                               dev->name,__FUNCTION__);
+#endif
+                        goto NextFrame;
+                }
+
+                if (link_num==0) {
+                        claw_process_control(dev, p_this_ccw);
+#ifdef DEBUGMSG
+                        printk(KERN_INFO "%s:%s goto next "
+                               "frame from claw_process_control \n",
+                               dev->name,__FUNCTION__);
+#endif
+                       CLAW_DBF_TEXT(4,trace,"UnpkCntl");
+                        goto NextFrame;
+                }
+unpack_next:
+               if (p_env->packing == DO_PACKED) {
+                       if (pack_off > p_env->read_size)
+                               goto NextFrame;
+                       p_packd = p_this_ccw->p_buffer+pack_off;
+                       p_packh = (struct clawph *) p_packd;
+                       if ((p_packh->len == 0) || /* all done with this frame? */
+                           (p_packh->flag != 0))
+                               goto NextFrame;
+                       bytes_to_mov = p_packh->len;
+                       pack_off += bytes_to_mov+sizeof(struct clawph);
+                       p++;
+               } else {
+                       bytes_to_mov=p_this_ccw->header.length;
+               }
+                if (privptr->mtc_logical_link<0) {
+#ifdef DEBUGMSG
+                printk(KERN_INFO "%s: %s mtc_logical_link < 0  \n",
+                       dev->name,__FUNCTION__);
+#endif
+
+                /*
+                *  if More-To-Come is set in this frame then we don't know
+                *  length of entire message, and hence have to allocate
+               *  large buffer   */
+
+                /*      We are starting a new envelope  */
+                privptr->mtc_offset=0;
+                        privptr->mtc_logical_link=link_num;
+                }
+
+                if (bytes_to_mov > (MAX_ENVELOPE_SIZE- privptr->mtc_offset) ) {
+                        /*      error     */
+#ifdef DEBUGMSG
+                        printk(KERN_INFO "%s: %s > goto next "
+                               "frame from MoretoComeSkip \n",
+                               dev->name,
+                               __FUNCTION__);
+                        printk(KERN_INFO "      bytes_to_mov %d > (MAX_ENVELOPE_"
+                               "SIZE-privptr->mtc_offset %d)\n",
+                               bytes_to_mov,(MAX_ENVELOPE_SIZE- privptr->mtc_offset));
+#endif
+                        privptr->stats.rx_frame_errors++;
+                        goto NextFrame;
+                }
+               if (p_env->packing == DO_PACKED) {
+                       memcpy( privptr->p_mtc_envelope+ privptr->mtc_offset,
+                               p_packd+sizeof(struct clawph), bytes_to_mov);
+
+               } else  {
+                       memcpy( privptr->p_mtc_envelope+ privptr->mtc_offset,
+                               p_this_ccw->p_buffer, bytes_to_mov);
+               }
+#ifdef DEBUGMSG
+                printk(KERN_INFO "%s: %s() received data \n",
+                       dev->name,__FUNCTION__);
+               if (p_env->packing == DO_PACKED)
+                       dumpit((char *)p_packd+sizeof(struct clawph),32);
+               else
+                       dumpit((char *)p_this_ccw->p_buffer, 32);
+               printk(KERN_INFO "%s: %s() bytelength %d \n",
+                       dev->name,__FUNCTION__,bytes_to_mov);
+#endif
+                if (mtc_this_frm==0) {
+                        len_of_data=privptr->mtc_offset+bytes_to_mov;
+                        skb=dev_alloc_skb(len_of_data);
+                        if (skb) {
+                                memcpy(skb_put(skb,len_of_data),
+                                       privptr->p_mtc_envelope,
+                                       len_of_data);
+                                skb->mac.raw=skb->data;
+                                skb->dev=dev;
+                                skb->protocol=htons(ETH_P_IP);
+                                skb->ip_summed=CHECKSUM_UNNECESSARY;
+                                privptr->stats.rx_packets++;
+                               privptr->stats.rx_bytes+=len_of_data;
+                                netif_rx(skb);
+#ifdef DEBUGMSG
+                                printk(KERN_INFO "%s: %s() netif_"
+                                       "rx(skb) completed \n",
+                                       dev->name,__FUNCTION__);
+#endif
+                        }
+                        else {
+                                privptr->stats.rx_dropped++;
+                                printk(KERN_WARNING "%s: %s() low on memory\n",
+                               dev->name,__FUNCTION__);
+                        }
+                        privptr->mtc_offset=0;
+                        privptr->mtc_logical_link=-1;
+                }
+                else {
+                        privptr->mtc_offset+=bytes_to_mov;
+                }
+               if (p_env->packing == DO_PACKED)
+                       goto unpack_next;
+NextFrame:
+                /*
+                *   Remove ThisCCWblock from active read queue, and add it
+                *   to queue of free blocks to be reused.
+                */
+                i++;
+                p_this_ccw->header.length=0xffff;
+                p_this_ccw->header.opcode=0xff;
+                /*
+                *       add this one to the free queue for later reuse
+                */
+                if (p_first_ccw==NULL) {
+                        p_first_ccw = p_this_ccw;
+                }
+                else {
+                        p_last_ccw->next = p_this_ccw;
+                }
+                p_last_ccw = p_this_ccw;
+                /*
+                *       chain to next block on active read queue
+                */
+                p_this_ccw = privptr->p_read_active_first;
+               CLAW_DBF_TEXT_(4,trace,"rxpkt %d",p);
+        } /* end of while */
+
+        /*      check validity                  */
+
+#ifdef IOTRACE
+        printk(KERN_INFO "%s:%s processed frame is %d \n",
+               dev->name,__FUNCTION__,i);
+        printk(KERN_INFO "%s:%s  F:%lx L:%lx\n",
+               dev->name,
+               __FUNCTION__,
+               (unsigned long)p_first_ccw,
+               (unsigned long)p_last_ccw);
+#endif
+       CLAW_DBF_TEXT_(4,trace,"rxfrm %d",i);
+        add_claw_reads(dev, p_first_ccw, p_last_ccw);
+        p_ch=&privptr->channel[READ];
+        claw_strt_read(dev, LOCK_YES);
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s: %s exit on line %d\n",
+               dev->name, __FUNCTION__, __LINE__);
+#endif
+        return;
+}     /*  end of unpack_read   */
+
+/*-------------------------------------------------------------------*
+*       claw_strt_read                                               *
+*                                                                    *
+*--------------------------------------------------------------------*/
+static void
+claw_strt_read (struct net_device *dev, int lock )
+{
+        int        rc = 0;
+        __u32      parm;
+        unsigned long  saveflags = 0;
+        struct claw_privbk *privptr=dev->priv;
+        struct ccwbk*p_ccwbk;
+        struct chbk *p_ch;
+        struct clawh *p_clawh;
+        p_ch=&privptr->channel[READ];
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter  \n",dev->name,__FUNCTION__);
+        printk(KERN_INFO "%s: variable lock = %d, dev =\n",dev->name, lock);
+        dumpit((char *) dev, sizeof(struct net_device));
+#endif
+       CLAW_DBF_TEXT(4,trace,"StRdNter");
+        p_clawh=(struct clawh *)privptr->p_claw_signal_blk;
+        p_clawh->flag=CLAW_IDLE;    /* 0x00 */
+
+        if ((privptr->p_write_active_first!=NULL &&
+             privptr->p_write_active_first->header.flag!=CLAW_PENDING) ||
+            (privptr->p_read_active_first!=NULL &&
+             privptr->p_read_active_first->header.flag!=CLAW_PENDING )) {
+                p_clawh->flag=CLAW_BUSY;    /* 0xff */
+        }
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s:%s state-%02x\n" ,
+               dev->name,__FUNCTION__, p_ch->claw_state);
+#endif
+        if (lock==LOCK_YES) {
+                spin_lock_irqsave(get_ccwdev_lock(p_ch->cdev), saveflags);
+        }
+        if (test_and_set_bit(0, (void *)&p_ch->IO_active) == 0) {
+#ifdef DEBUGMSG
+                printk(KERN_INFO "%s: HOT READ started in %s\n" ,
+                       dev->name,__FUNCTION__);
+                p_clawh=(struct clawh *)privptr->p_claw_signal_blk;
+                dumpit((char *)&p_clawh->flag , 1);
+#endif
+               CLAW_DBF_TEXT(4,trace,"HotRead");
+                p_ccwbk=privptr->p_read_active_first;
+                parm = (unsigned long) p_ch;
+                rc = ccw_device_start (p_ch->cdev, &p_ccwbk->read, parm,
+                                      0xff, 0);
+                if (rc != 0) {
+                        ccw_check_return_code(p_ch->cdev, rc);
+                }
+        }
+       else {
+#ifdef DEBUGMSG
+               printk(KERN_INFO "%s: No READ started by %s() In progress\n" ,
+                       dev->name,__FUNCTION__);
+#endif
+               CLAW_DBF_TEXT(2,trace,"ReadAct");
+       }
+
+        if (lock==LOCK_YES) {
+                spin_unlock_irqrestore(get_ccwdev_lock(p_ch->cdev), saveflags);
+        }
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Exit on line %d\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+       CLAW_DBF_TEXT(4,trace,"StRdExit");
+        return;
+}       /*    end of claw_strt_read    */
+
+/*-------------------------------------------------------------------*
+*       claw_strt_out_IO                                             *
+*                                                                    *
+*--------------------------------------------------------------------*/
+
+static void
+claw_strt_out_IO( struct net_device *dev )
+{
+        int                    rc = 0;
+        unsigned long          parm;
+        struct claw_privbk     *privptr;
+        struct chbk            *p_ch;
+        struct ccwbk           *p_first_ccw;
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
+#endif
+       if (!dev) {
+               return;
+       }
+        privptr=(struct claw_privbk *)dev->priv;
+        p_ch=&privptr->channel[WRITE];
+
+#ifdef DEBUGMSG
+        printk(KERN_INFO "%s:%s state-%02x\n" ,
+               dev->name,__FUNCTION__,p_ch->claw_state);
+#endif
+        CLAW_DBF_TEXT(4,trace,"strt_io");
+        p_first_ccw=privptr->p_write_active_first;
+
+        if (p_ch->claw_state == CLAW_STOP)
+                return;
+        if (p_first_ccw == NULL) {
+#ifdef FUNCTRACE
+                printk(KERN_INFO "%s:%s Exit on line %d\n",
+                       dev->name,__FUNCTION__,__LINE__);
+#endif
+                return;
+        }
+        if (test_and_set_bit(0, (void *)&p_ch->IO_active) == 0) {
+                parm = (unsigned long) p_ch;
+#ifdef DEBUGMSG
+                printk(KERN_INFO "%s:%s do_io \n" ,dev->name,__FUNCTION__);
+                dumpit((char *)p_first_ccw, sizeof(struct ccwbk));
+#endif
+               CLAW_DBF_TEXT(2,trace,"StWrtIO");
+                rc = ccw_device_start (p_ch->cdev,&p_first_ccw->write, parm,
+                                      0xff, 0);
+                if (rc != 0) {
+                        ccw_check_return_code(p_ch->cdev, rc);
+                }
+        }
+        dev->trans_start = jiffies;
+#ifdef FUNCTRACE
+       printk(KERN_INFO "%s:%s Exit on line %d\n",
+               dev->name,__FUNCTION__,__LINE__);
+#endif
+
+        return;
+}       /*    end of claw_strt_out_IO    */
+
+/*-------------------------------------------------------------------*
+*       Free write buffers                                           *
+*                                                                    *
+*--------------------------------------------------------------------*/
+
+static void
+claw_free_wrt_buf( struct net_device *dev )
+{
+
+        struct claw_privbk *privptr=(struct claw_privbk *)dev->priv;
+        struct ccwbk*p_first_ccw;
+       struct ccwbk*p_last_ccw;
+       struct ccwbk*p_this_ccw;
+       struct ccwbk*p_next_ccw;
+#ifdef IOTRACE
+        struct ccwbk*p_buf;
+#endif
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
+        printk(KERN_INFO "%s: free count = %d  variable dev =\n",
+               dev->name,privptr->write_free_count);
+#endif
+       CLAW_DBF_TEXT(4,trace,"freewrtb");
+        /*  scan the write queue to free any completed write packets   */
+        p_first_ccw=NULL;
+        p_last_ccw=NULL;
+#ifdef IOTRACE
+        printk(KERN_INFO "%s:  Dump current CCW chain \n",dev->name  );
+        p_buf=privptr->p_write_active_first;
+        while (p_buf!=NULL) {
+                dumpit((char *)p_buf, sizeof(struct ccwbk));
+                p_buf=p_buf->next;
+        }
+        if (p_buf==NULL) {
+                printk(KERN_INFO "%s: privptr->p_write_"
+                       "active_first==NULL\n",dev->name  );
+        }
+        p_buf=(struct ccwbk*)privptr->p_end_ccw;
+        dumpit((char *)p_buf, sizeof(struct endccw));
+#endif
+        p_this_ccw=privptr->p_write_active_first;
+        while ( (p_this_ccw!=NULL) && (p_this_ccw->header.flag!=CLAW_PENDING))
+        {
+                p_next_ccw = p_this_ccw->next;
+                if (((p_next_ccw!=NULL) &&
+                    (p_next_ccw->header.flag!=CLAW_PENDING)) ||
+                    ((p_this_ccw == privptr->p_write_active_last) &&
+                     (p_this_ccw->header.flag!=CLAW_PENDING))) {
+                        /* The next CCW is OK or this is  */
+                       /* the last CCW...free it   @A1A  */
+                        privptr->p_write_active_first=p_this_ccw->next;
+                       p_this_ccw->header.flag=CLAW_PENDING;
+                        p_this_ccw->next=privptr->p_write_free_chain;
+                       privptr->p_write_free_chain=p_this_ccw;
+                        ++privptr->write_free_count;
+                       privptr->stats.tx_bytes+= p_this_ccw->write.count;
+                       p_this_ccw=privptr->p_write_active_first;
+                        privptr->stats.tx_packets++;
+                }
+                else {
+                       break;
+                }
+        }
+        if (privptr->write_free_count!=0) {
+                claw_clearbit_busy(TB_NOBUFFER,dev);
+        }
+        /*   whole chain removed?   */
+        if (privptr->p_write_active_first==NULL) {
+                privptr->p_write_active_last=NULL;
+#ifdef DEBUGMSG
+                printk(KERN_INFO "%s:%s p_write_"
+                       "active_first==NULL\n",dev->name,__FUNCTION__);
+#endif
+        }
+#ifdef IOTRACE
+        printk(KERN_INFO "%s: Dump arranged CCW chain \n",dev->name  );
+        p_buf=privptr->p_write_active_first;
+        while (p_buf!=NULL) {
+                dumpit((char *)p_buf, sizeof(struct ccwbk));
+                p_buf=p_buf->next;
+        }
+        if (p_buf==NULL) {
+                printk(KERN_INFO "%s: privptr->p_write_active_"
+                       "first==NULL\n",dev->name  );
+        }
+        p_buf=(struct ccwbk*)privptr->p_end_ccw;
+        dumpit((char *)p_buf, sizeof(struct endccw));
+#endif
+
+       CLAW_DBF_TEXT_(4,trace,"FWC=%d",privptr->write_free_count);
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Exit on line %d free_count =%d\n",
+               dev->name,__FUNCTION__, __LINE__,privptr->write_free_count);
+#endif
+        return;
+}
+
+/*-------------------------------------------------------------------*
+*       claw free netdevice                                          *
+*                                                                    *
+*--------------------------------------------------------------------*/
+static void
+claw_free_netdevice(struct net_device * dev, int free_dev)
+{
+       struct claw_privbk *privptr;
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(2,setup,"free_dev");
+
+       if (!dev)
+               return;
+       CLAW_DBF_TEXT_(2,setup,"%s",dev->name);
+       privptr = dev->priv;
+       if (dev->flags & IFF_RUNNING)
+               claw_release(dev);
+       if (privptr) {
+               privptr->channel[READ].ndev = NULL;  /* say it's free */
+       }
+       dev->priv=NULL;
+#ifdef MODULE
+       if (free_dev) {
+               free_netdev(dev);
+       }
+#endif
+       CLAW_DBF_TEXT(2,setup,"feee_ok");
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Exit\n",dev->name,__FUNCTION__);
+#endif
+}
+
+/**
+ * Claw init netdevice
+ * Initialize everything of the net device except the name and the
+ * channel structs.
+ */
+static void
+claw_init_netdevice(struct net_device * dev)
+{
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter\n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(2,setup,"init_dev");
+       CLAW_DBF_TEXT_(2,setup,"%s",dev->name);
+       if (!dev) {
+        printk(KERN_WARNING "claw:%s BAD Device exit line %d\n",
+               __FUNCTION__,__LINE__);
+               CLAW_DBF_TEXT(2,setup,"baddev");
+               return;
+       }
+       dev->mtu = CLAW_DEFAULT_MTU_SIZE;
+       dev->hard_start_xmit = claw_tx;
+       dev->open = claw_open;
+       dev->stop = claw_release;
+       dev->get_stats = claw_stats;
+       dev->change_mtu = claw_change_mtu;
+       dev->hard_header_len = 0;
+       dev->addr_len = 0;
+       dev->type = ARPHRD_SLIP;
+       dev->tx_queue_len = 1300;
+       dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+       SET_MODULE_OWNER(dev);
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Exit\n",dev->name,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT(2,setup,"initok");
+       return;
+}
+
+/**
+ * Init a new channel in the privptr->channel[i].
+ *
+ * @param cdev  The ccw_device to be added.
+ *
+ * @return 0 on success, !0 on error.
+ */
+static int
+add_channel(struct ccw_device *cdev,int i,struct claw_privbk *privptr)
+{
+       struct chbk *p_ch;
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "%s:%s Enter\n",cdev->dev.bus_id,__FUNCTION__);
+#endif
+       CLAW_DBF_TEXT_(2,setup,"%s",cdev->dev.bus_id);
+       privptr->channel[i].flag  = i+1;   /* Read is 1 Write is 2 */
+       p_ch = &privptr->channel[i];
+       p_ch->cdev = cdev;
+       snprintf(p_ch->id, CLAW_ID_SIZE, "cl-%s", cdev->dev.bus_id);
+       sscanf(cdev->dev.bus_id+4,"%x",&p_ch->devno);
+       if ((p_ch->irb = kmalloc(sizeof (struct irb),GFP_KERNEL)) == NULL) {
+               printk(KERN_WARNING "%s Out of memory in %s for irb\n",
+                       p_ch->id,__FUNCTION__);
+#ifdef FUNCTRACE
+               printk(KERN_INFO "%s:%s Exit on line %d\n",
+                       p_ch->id,__FUNCTION__,__LINE__);
+#endif
+               return -ENOMEM;
+       }
+       memset(p_ch->irb, 0, sizeof (struct irb));
+#ifdef FUNCTRACE
+               printk(KERN_INFO "%s:%s Exit on line %d\n",
+                       cdev->dev.bus_id,__FUNCTION__,__LINE__);
+#endif
+       return 0;
+}
+
+
+/**
+ *
+ * Setup an interface.
+ *
+ * @param cgdev  Device to be setup.
+ *
+ * @returns 0 on success, !0 on failure.
+ */
+static int
+claw_new_device(struct ccwgroup_device *cgdev)
+{
+       struct claw_privbk *privptr;
+       struct claw_env *p_env;
+       struct net_device *dev;
+       int ret;
+
+       pr_debug("%s() called\n", __FUNCTION__);
+       printk(KERN_INFO "claw: add for %s\n",cgdev->cdev[READ]->dev.bus_id);
+       CLAW_DBF_TEXT(2,setup,"new_dev");
+       privptr = cgdev->dev.driver_data;
+       cgdev->cdev[READ]->dev.driver_data = privptr;
+       cgdev->cdev[WRITE]->dev.driver_data = privptr;
+       if (!privptr)
+               return -ENODEV;
+       p_env = privptr->p_env;
+       sscanf(cgdev->cdev[READ]->dev.bus_id+4,"%x",
+               &p_env->devno[READ]);
+        sscanf(cgdev->cdev[WRITE]->dev.bus_id+4,"%x",
+               &p_env->devno[WRITE]);
+       ret = add_channel(cgdev->cdev[0],0,privptr);
+       if (ret == 0)
+               ret = add_channel(cgdev->cdev[1],1,privptr);
+       if (ret != 0) {
+                       printk(KERN_WARNING
+                       "add channel failed "
+                               "with ret = %d\n", ret);
+                       goto out;
+       }
+       ret = ccw_device_set_online(cgdev->cdev[READ]);
+       if (ret != 0) {
+               printk(KERN_WARNING
+                "claw: ccw_device_set_online %s READ failed "
+                       "with ret = %d\n",cgdev->cdev[READ]->dev.bus_id,ret);
+               goto out;
+       }
+       ret = ccw_device_set_online(cgdev->cdev[WRITE]);
+       if (ret != 0) {
+               printk(KERN_WARNING
+                "claw: ccw_device_set_online %s WRITE failed "
+                       "with ret = %d\n",cgdev->cdev[WRITE]->dev.bus_id, ret);
+               goto out;
+       }
+       dev = alloc_netdev(0,"claw%d",claw_init_netdevice);
+       if (!dev) {
+               printk(KERN_WARNING "%s:alloc_netdev failed\n",__FUNCTION__);
+               goto out;
+       }
+       dev->priv = privptr;
+       cgdev->dev.driver_data = privptr;
+        cgdev->cdev[READ]->dev.driver_data = privptr;
+        cgdev->cdev[WRITE]->dev.driver_data = privptr;
+       /* sysfs magic */
+        SET_NETDEV_DEV(dev, &cgdev->dev);
+       if (register_netdev(dev) != 0) {
+               claw_free_netdevice(dev, 1);
+               CLAW_DBF_TEXT(2,trace,"regfail");
+               goto out;
+       }
+       dev->flags &=~IFF_RUNNING;
+       if (privptr->buffs_alloc == 0) {
+               ret=init_ccw_bk(dev);
+               if (ret !=0) {
+                       printk(KERN_WARNING
+                        "claw: init_ccw_bk failed with ret=%d\n", ret);
+                       unregister_netdev(dev);
+                       claw_free_netdevice(dev,1);
+                       CLAW_DBF_TEXT(2,trace,"ccwmem");
+                       goto out;
+               }
+       }
+       privptr->channel[READ].ndev = dev;
+       privptr->channel[WRITE].ndev = dev;
+       privptr->p_env->ndev = dev;
+
+       printk(KERN_INFO "%s:readsize=%d  writesize=%d "
+               "readbuffer=%d writebuffer=%d read=0x%04x write=0x%04x\n",
+                dev->name, p_env->read_size,
+               p_env->write_size, p_env->read_buffers,
+                p_env->write_buffers, p_env->devno[READ],
+               p_env->devno[WRITE]);
+        printk(KERN_INFO "%s:host_name:%.8s, adapter_name "
+               ":%.8s api_type: %.8s\n",
+                dev->name, p_env->host_name,
+               p_env->adapter_name , p_env->api_type);
+       return 0;
+out:
+       ccw_device_set_offline(cgdev->cdev[1]);
+       ccw_device_set_offline(cgdev->cdev[0]);
+
+       return -ENODEV;
+}
+
+static void
+claw_purge_skb_queue(struct sk_buff_head *q)
+{
+        struct sk_buff *skb;
+
+        CLAW_DBF_TEXT(4,trace,"purgque");
+
+        while ((skb = skb_dequeue(q))) {
+                atomic_dec(&skb->users);
+                dev_kfree_skb_irq(skb);
+        }
+}
+
+/**
+ * Shutdown an interface.
+ *
+ * @param cgdev  Device to be shut down.
+ *
+ * @returns 0 on success, !0 on failure.
+ */
+static int
+claw_shutdown_device(struct ccwgroup_device *cgdev)
+{
+       struct claw_privbk *priv;
+       struct net_device *ndev;
+       int     ret;
+
+       pr_debug("%s() called\n", __FUNCTION__);
+       CLAW_DBF_TEXT_(2,setup,"%s",cgdev->dev.bus_id);
+       priv = cgdev->dev.driver_data;
+       if (!priv)
+               return -ENODEV;
+       ndev = priv->channel[READ].ndev;
+       if (ndev) {
+               /* Close the device */
+               printk(KERN_INFO
+                       "%s: shuting down \n",ndev->name);
+               if (ndev->flags & IFF_RUNNING)
+                       ret = claw_release(ndev);
+               ndev->flags &=~IFF_RUNNING;
+               unregister_netdev(ndev);
+               ndev->priv = NULL;  /* cgdev data, not ndev's to free */
+               claw_free_netdevice(ndev, 1);
+               priv->channel[READ].ndev = NULL;
+               priv->channel[WRITE].ndev = NULL;
+               priv->p_env->ndev = NULL;
+       }
+       ccw_device_set_offline(cgdev->cdev[1]);
+       ccw_device_set_offline(cgdev->cdev[0]);
+       return 0;
+}
+
+static void
+claw_remove_device(struct ccwgroup_device *cgdev)
+{
+       struct claw_privbk *priv;
+
+       pr_debug("%s() called\n", __FUNCTION__);
+       CLAW_DBF_TEXT_(2,setup,"%s",cgdev->dev.bus_id);
+       priv = cgdev->dev.driver_data;
+       if (!priv) {
+               printk(KERN_WARNING "claw: %s() no Priv exiting\n",__FUNCTION__);
+               return;
+       }
+       printk(KERN_INFO "claw: %s() called %s will be removed.\n",
+                       __FUNCTION__,cgdev->cdev[0]->dev.bus_id);
+       if (cgdev->state == CCWGROUP_ONLINE)
+               claw_shutdown_device(cgdev);
+       claw_remove_files(&cgdev->dev);
+       if (priv->p_mtc_envelope!=NULL) {
+                kfree(priv->p_mtc_envelope);
+                priv->p_mtc_envelope=NULL;
+        }
+       if (priv->p_env != NULL) {
+               kfree(priv->p_env);
+               priv->p_env=NULL;
+       }
+       if (priv->channel[0].irb != NULL) {
+               kfree(priv->channel[0].irb);
+               priv->channel[0].irb=NULL;
+       }
+       if (priv->channel[1].irb != NULL) {
+               kfree(priv->channel[1].irb);
+               priv->channel[1].irb=NULL;
+       }
+       kfree(priv);
+       cgdev->dev.driver_data=NULL;
+       cgdev->cdev[READ]->dev.driver_data = NULL;
+       cgdev->cdev[WRITE]->dev.driver_data = NULL;
+       put_device(&cgdev->dev);
+}
+
+
+/*
+ * sysfs attributes
+ */
+static ssize_t
+claw_hname_show(struct device *dev, char *buf)
+{
+       struct claw_privbk *priv;
+       struct claw_env *  p_env;
+
+       priv = dev->driver_data;
+       if (!priv)
+               return -ENODEV;
+       p_env = priv->p_env;
+       return sprintf(buf, "%s\n",p_env->host_name);
+}
+
+static ssize_t
+claw_hname_write(struct device *dev, const char *buf, size_t count)
+{
+       struct claw_privbk *priv;
+       struct claw_env *  p_env;
+
+       priv = dev->driver_data;
+       if (!priv)
+               return -ENODEV;
+       p_env = priv->p_env;
+       if (count > MAX_NAME_LEN+1)
+               return -EINVAL;
+       memset(p_env->host_name, 0x20, MAX_NAME_LEN);
+       strncpy(p_env->host_name,buf, count);
+       p_env->host_name[count-1] = 0x20;  /* clear extra 0x0a */
+       p_env->host_name[MAX_NAME_LEN] = 0x00;
+       CLAW_DBF_TEXT(2,setup,"HstnSet");
+        CLAW_DBF_TEXT_(2,setup,"%s",p_env->host_name);
+
+       return count;
+}
+
+static DEVICE_ATTR(host_name, 0644, claw_hname_show, claw_hname_write);
+
+static ssize_t
+claw_adname_show(struct device *dev, char *buf)
+{
+       struct claw_privbk *priv;
+       struct claw_env *  p_env;
+
+       priv = dev->driver_data;
+       if (!priv)
+               return -ENODEV;
+       p_env = priv->p_env;
+       return sprintf(buf, "%s\n",p_env->adapter_name);
+}
+
+static ssize_t
+claw_adname_write(struct device *dev, const char *buf, size_t count)
+{
+       struct claw_privbk *priv;
+       struct claw_env *  p_env;
+
+       priv = dev->driver_data;
+       if (!priv)
+               return -ENODEV;
+       p_env = priv->p_env;
+       if (count > MAX_NAME_LEN+1)
+               return -EINVAL;
+       memset(p_env->adapter_name, 0x20, MAX_NAME_LEN);
+       strncpy(p_env->adapter_name,buf, count);
+       p_env->adapter_name[count-1] = 0x20; /* clear extra 0x0a */
+       p_env->adapter_name[MAX_NAME_LEN] = 0x00;
+       CLAW_DBF_TEXT(2,setup,"AdnSet");
+       CLAW_DBF_TEXT_(2,setup,"%s",p_env->adapter_name);
+
+       return count;
+}
+
+static DEVICE_ATTR(adapter_name, 0644, claw_adname_show, claw_adname_write);
+
+static ssize_t
+claw_apname_show(struct device *dev, char *buf)
+{
+       struct claw_privbk *priv;
+       struct claw_env *  p_env;
+
+       priv = dev->driver_data;
+       if (!priv)
+               return -ENODEV;
+       p_env = priv->p_env;
+       return sprintf(buf, "%s\n",
+                      p_env->api_type);
+}
+
+static ssize_t
+claw_apname_write(struct device *dev, const char *buf, size_t count)
+{
+       struct claw_privbk *priv;
+       struct claw_env *  p_env;
+
+       priv = dev->driver_data;
+       if (!priv)
+               return -ENODEV;
+       p_env = priv->p_env;
+       if (count > MAX_NAME_LEN+1)
+               return -EINVAL;
+       memset(p_env->api_type, 0x20, MAX_NAME_LEN);
+       strncpy(p_env->api_type,buf, count);
+       p_env->api_type[count-1] = 0x20;  /* we get a loose 0x0a */
+       p_env->api_type[MAX_NAME_LEN] = 0x00;
+       if(strncmp(p_env->api_type,WS_APPL_NAME_PACKED,6) == 0) {
+               p_env->read_size=DEF_PACK_BUFSIZE;
+               p_env->write_size=DEF_PACK_BUFSIZE;
+               p_env->packing=PACKING_ASK;
+               CLAW_DBF_TEXT(2,setup,"PACKING");
+       }
+       else {
+               p_env->packing=0;
+               p_env->read_size=CLAW_FRAME_SIZE;
+               p_env->write_size=CLAW_FRAME_SIZE;
+               CLAW_DBF_TEXT(2,setup,"ApiSet");
+       }
+       CLAW_DBF_TEXT_(2,setup,"%s",p_env->api_type);
+       return count;
+}
+
+static DEVICE_ATTR(api_type, 0644, claw_apname_show, claw_apname_write);
+
+static ssize_t
+claw_wbuff_show(struct device *dev, char *buf)
+{
+       struct claw_privbk *priv;
+       struct claw_env * p_env;
+
+       priv = dev->driver_data;
+       if (!priv)
+               return -ENODEV;
+       p_env = priv->p_env;
+       return sprintf(buf, "%d\n", p_env->write_buffers);
+}
+
+static ssize_t
+claw_wbuff_write(struct device *dev, const char *buf, size_t count)
+{
+       struct claw_privbk *priv;
+       struct claw_env *  p_env;
+       int nnn,max;
+
+       priv = dev->driver_data;
+       if (!priv)
+               return -ENODEV;
+       p_env = priv->p_env;
+       sscanf(buf, "%i", &nnn);
+       if (p_env->packing) {
+               max = 64;
+       }
+       else {
+               max = 512;
+       }
+       if ((nnn > max ) || (nnn < 2))
+               return -EINVAL;
+       p_env->write_buffers = nnn;
+       CLAW_DBF_TEXT(2,setup,"Wbufset");
+        CLAW_DBF_TEXT_(2,setup,"WB=%d",p_env->write_buffers);
+       return count;
+}
+
+static DEVICE_ATTR(write_buffer, 0644, claw_wbuff_show, claw_wbuff_write);
+
+static ssize_t
+claw_rbuff_show(struct device *dev, char *buf)
+{
+       struct claw_privbk *priv;
+       struct claw_env *  p_env;
+
+       priv = dev->driver_data;
+       if (!priv)
+               return -ENODEV;
+       p_env = priv->p_env;
+       return sprintf(buf, "%d\n", p_env->read_buffers);
+}
+
+static ssize_t
+claw_rbuff_write(struct device *dev, const char *buf, size_t count)
+{
+       struct claw_privbk *priv;
+       struct claw_env *p_env;
+       int nnn,max;
+
+       priv = dev->driver_data;
+       if (!priv)
+               return -ENODEV;
+       p_env = priv->p_env;
+       sscanf(buf, "%i", &nnn);
+       if (p_env->packing) {
+               max = 64;
+       }
+       else {
+               max = 512;
+       }
+       if ((nnn > max ) || (nnn < 2))
+               return -EINVAL;
+       p_env->read_buffers = nnn;
+       CLAW_DBF_TEXT(2,setup,"Rbufset");
+       CLAW_DBF_TEXT_(2,setup,"RB=%d",p_env->read_buffers);
+       return count;
+}
+
+static DEVICE_ATTR(read_buffer, 0644, claw_rbuff_show, claw_rbuff_write);
+
+static struct attribute *claw_attr[] = {
+       &dev_attr_read_buffer.attr,
+       &dev_attr_write_buffer.attr,
+       &dev_attr_adapter_name.attr,
+       &dev_attr_api_type.attr,
+       &dev_attr_host_name.attr,
+       NULL,
+};
+
+static struct attribute_group claw_attr_group = {
+       .attrs = claw_attr,
+};
+
+static int
+claw_add_files(struct device *dev)
+{
+       pr_debug("%s() called\n", __FUNCTION__);
+       CLAW_DBF_TEXT(2,setup,"add_file");
+       return sysfs_create_group(&dev->kobj, &claw_attr_group);
+}
+
+static void
+claw_remove_files(struct device *dev)
+{
+       pr_debug("%s() called\n", __FUNCTION__);
+       CLAW_DBF_TEXT(2,setup,"rem_file");
+       sysfs_remove_group(&dev->kobj, &claw_attr_group);
+}
+
+/*--------------------------------------------------------------------*
+*    claw_init  and cleanup                                           *
+*---------------------------------------------------------------------*/
+
+static void __exit
+claw_cleanup(void)
+{
+       unregister_cu3088_discipline(&claw_group_driver);
+       claw_unregister_debug_facility();
+       printk(KERN_INFO "claw: Driver unloaded\n");
+
+}
+
+/**
+ * Initialize module.
+ * This is called just after the module is loaded.
+ *
+ * @return 0 on success, !0 on error.
+ */
+static int __init
+claw_init(void)
+{
+       int ret = 0;
+       printk(KERN_INFO "claw: starting driver "
+#ifdef MODULE
+                "module "
+#else
+                "compiled into kernel "
+#endif
+                " $Revision: 1.35 $ $Date: 2005/03/24 12:25:38 $ \n");
+
+
+#ifdef FUNCTRACE
+        printk(KERN_INFO "claw: %s() enter \n",__FUNCTION__);
+#endif
+       ret = claw_register_debug_facility();
+       if (ret) {
+               printk(KERN_WARNING "claw: %s() debug_register failed %d\n",
+                       __FUNCTION__,ret);
+               return ret;
+       }
+       CLAW_DBF_TEXT(2,setup,"init_mod");
+       ret = register_cu3088_discipline(&claw_group_driver);
+       if (ret) {
+               claw_unregister_debug_facility();
+               printk(KERN_WARNING "claw; %s() cu3088 register failed %d\n",
+                       __FUNCTION__,ret);
+       }
+#ifdef FUNCTRACE
+        printk(KERN_INFO "claw: %s() exit \n",__FUNCTION__);
+#endif
+       return ret;
+}
+
+module_init(claw_init);
+module_exit(claw_cleanup);
+
+
+
+/*--------------------------------------------------------------------*
+*    End of File                                                      *
+*---------------------------------------------------------------------*/
+
+
diff --git a/drivers/s390/net/claw.h b/drivers/s390/net/claw.h
new file mode 100644 (file)
index 0000000..3df7197
--- /dev/null
@@ -0,0 +1,335 @@
+/*******************************************************
+*  Define constants                                    *
+*                                                      *
+********************************************************/
+#define VERSION_CLAW_H "$Revision: 1.6 $"
+/*-----------------------------------------------------*
+*     CCW command codes for CLAW protocol              *
+*------------------------------------------------------*/
+
+#define CCW_CLAW_CMD_WRITE           0x01      /* write - not including link */
+#define CCW_CLAW_CMD_READ            0x02      /* read */
+#define CCW_CLAW_CMD_NOP             0x03      /* NOP */
+#define CCW_CLAW_CMD_SENSE           0x04      /* Sense */
+#define CCW_CLAW_CMD_SIGNAL_SMOD     0x05      /* Signal Status Modifier */
+#define CCW_CLAW_CMD_TIC             0x08      /* TIC */
+#define CCW_CLAW_CMD_READHEADER      0x12      /* read header data */
+#define CCW_CLAW_CMD_READFF          0x22      /* read an FF */
+#define CCW_CLAW_CMD_SENSEID         0xe4      /* Sense ID */
+
+
+/*-----------------------------------------------------*
+*    CLAW Unique constants                             *
+*------------------------------------------------------*/
+
+#define MORE_to_COME_FLAG       0x04   /* OR with write CCW in case of m-t-c */
+#define CLAW_IDLE               0x00   /* flag to indicate CLAW is idle */
+#define CLAW_BUSY               0xff   /* flag to indicate CLAW is busy */
+#define CLAW_PENDING            0x00   /* flag to indicate i/o is pending */
+#define CLAW_COMPLETE           0xff   /* flag to indicate i/o completed */
+
+/*-----------------------------------------------------*
+*     CLAW control comand code                         *
+*------------------------------------------------------*/
+
+#define SYSTEM_VALIDATE_REQUEST   0x01  /* System Validate request */
+#define SYSTEM_VALIDATE_RESPONSE  0x02  /* System Validate response */
+#define CONNECTION_REQUEST        0x21  /* Connection request */
+#define CONNECTION_RESPONSE       0x22  /* Connection response */
+#define CONNECTION_CONFIRM        0x23  /* Connection confirm */
+#define DISCONNECT                0x24  /* Disconnect */
+#define CLAW_ERROR                0x41  /* CLAW error message */
+#define CLAW_VERSION_ID           2     /* CLAW version ID */
+
+/*-----------------------------------------------------*
+*  CLAW adater sense bytes                             *
+*------------------------------------------------------*/
+
+#define CLAW_ADAPTER_SENSE_BYTE 0x41   /* Stop command issued to adapter */
+
+/*-----------------------------------------------------*
+*      CLAW control command return codes               *
+*------------------------------------------------------*/
+
+#define CLAW_RC_NAME_MISMATCH       166  /*  names do not match */
+#define CLAW_RC_WRONG_VERSION       167  /*  wrong CLAW version number */
+#define CLAW_RC_HOST_RCV_TOO_SMALL  180  /*  Host maximum receive is   */
+                                        /*  less than Linux on zSeries*/
+                                         /*  transmit size             */
+
+/*-----------------------------------------------------*
+*      CLAW Constants application name                 *
+*------------------------------------------------------*/
+
+#define HOST_APPL_NAME          "TCPIP   "
+#define WS_APPL_NAME_IP_LINK    "TCPIP   "
+#define WS_APPL_NAME_IP_NAME   "IP      "
+#define WS_APPL_NAME_API_LINK   "API     "
+#define WS_APPL_NAME_PACKED     "PACKED  "
+#define WS_NAME_NOT_DEF         "NOT_DEF "
+#define PACKING_ASK            1
+#define PACK_SEND              2
+#define DO_PACKED              3
+
+#define MAX_ENVELOPE_SIZE       65536
+#define CLAW_DEFAULT_MTU_SIZE   4096
+#define DEF_PACK_BUFSIZE       32768
+#define READ                    0
+#define WRITE                   1
+
+#define TB_TX                   0          /* sk buffer handling in process  */
+#define TB_STOP                 1          /* network device stop in process */
+#define TB_RETRY                2          /* retry in process               */
+#define TB_NOBUFFER             3          /* no buffer on free queue        */
+#define CLAW_MAX_LINK_ID        1
+#define CLAW_MAX_DEV            256        /*      max claw devices          */
+#define MAX_NAME_LEN            8          /* host name, adapter name length */
+#define CLAW_FRAME_SIZE         4096
+#define CLAW_ID_SIZE            BUS_ID_SIZE+3
+
+/* state machine codes used in claw_irq_handler */
+
+#define CLAW_STOP                0
+#define CLAW_START_HALT_IO       1
+#define CLAW_START_SENSEID       2
+#define CLAW_START_READ          3
+#define CLAW_START_WRITE         4
+
+/*-----------------------------------------------------*
+*    Lock flag                                         *
+*------------------------------------------------------*/
+#define LOCK_YES             0
+#define LOCK_NO              1
+
+/*-----------------------------------------------------*
+*    DBF Debug macros                                  *
+*------------------------------------------------------*/
+#define CLAW_DBF_TEXT(level, name, text) \
+       do { \
+               debug_text_event(claw_dbf_##name, level, text); \
+       } while (0)
+
+#define CLAW_DBF_HEX(level,name,addr,len) \
+do { \
+       debug_event(claw_dbf_##name,level,(void*)(addr),len); \
+} while (0)
+
+#define CLAW_DBF_TEXT_(level,name,text...) \
+do {                                       \
+       sprintf(debug_buffer, text);  \
+               debug_text_event(claw_dbf_##name,level, debug_buffer);\
+} while (0)
+
+/*******************************************************
+*  Define Control Blocks                               *
+*                                                      *
+********************************************************/
+
+/*------------------------------------------------------*/
+/*     CLAW header                                      */
+/*------------------------------------------------------*/
+
+struct clawh {
+        __u16  length;     /* length of data read by preceding read CCW */
+        __u8   opcode;     /* equivalent read CCW */
+        __u8   flag;       /* flag of FF to indicate read was completed */
+};
+
+/*------------------------------------------------------*/
+/*     CLAW Packing header   4 bytes                    */
+/*------------------------------------------------------*/
+struct clawph {
+       __u16 len;      /* Length of Packed Data Area   */
+       __u8  flag;     /* Reserved not used            */
+       __u8  link_num; /* Link ID                      */
+};
+
+/*------------------------------------------------------*/
+/*     CLAW Ending struct ccwbk                         */
+/*------------------------------------------------------*/
+struct endccw {
+       __u32     real;            /* real address of this block */
+       __u8      write1;          /* write 1 is active */
+        __u8      read1;           /* read 1 is active  */
+        __u16     reserved;        /* reserved for future use */
+        struct ccw1    write1_nop1;
+        struct ccw1    write1_nop2;
+        struct ccw1    write2_nop1;
+        struct ccw1    write2_nop2;
+        struct ccw1    read1_nop1;
+        struct ccw1    read1_nop2;
+        struct ccw1    read2_nop1;
+        struct ccw1    read2_nop2;
+};
+
+/*------------------------------------------------------*/
+/*     CLAW struct ccwbk                                       */
+/*------------------------------------------------------*/
+struct ccwbk {
+        void   *next;        /* pointer to next ccw block */
+        __u32     real;         /* real address of this ccw */
+        void      *p_buffer;    /* virtual address of data */
+        struct clawh     header;       /* claw header */
+        struct ccw1    write;   /* write CCW    */
+        struct ccw1    w_read_FF; /* read FF */
+        struct ccw1    w_TIC_1;        /* TIC */
+        struct ccw1    read;         /* read CCW  */
+        struct ccw1    read_h;        /* read header */
+        struct ccw1    signal;       /* signal SMOD  */
+        struct ccw1    r_TIC_1;        /* TIC1 */
+        struct ccw1    r_read_FF;      /* read FF  */
+        struct ccw1    r_TIC_2;        /* TIC2 */
+};
+
+/*------------------------------------------------------*/
+/*     CLAW control block                               */
+/*------------------------------------------------------*/
+struct clawctl {
+        __u8    command;      /* control command */
+        __u8    version;      /* CLAW protocol version */
+        __u8    linkid;       /* link ID   */
+        __u8    correlator;   /* correlator */
+        __u8    rc;           /* return code */
+        __u8    reserved1;    /* reserved */
+        __u8    reserved2;    /* reserved */
+        __u8    reserved3;    /* reserved */
+        __u8    data[24];     /* command specific fields */
+};
+
+/*------------------------------------------------------*/
+/*     Data for SYSTEMVALIDATE command                  */
+/*------------------------------------------------------*/
+struct sysval  {
+        char    WS_name[8];        /* Workstation System name  */
+        char    host_name[8];      /* Host system name     */
+        __u16   read_frame_size;   /* read frame size */
+        __u16   write_frame_size;  /* write frame size */
+        __u8    reserved[4];       /* reserved */
+};
+
+/*------------------------------------------------------*/
+/*     Data for Connect command                         */
+/*------------------------------------------------------*/
+struct conncmd  {
+        char     WS_name[8];       /* Workstation application name  */
+        char     host_name[8];     /* Host application name      */
+        __u16    reserved1[2];     /* read frame size */
+        __u8     reserved2[4];     /* reserved  */
+};
+
+/*------------------------------------------------------*/
+/*     Data for CLAW error                              */
+/*------------------------------------------------------*/
+struct clawwerror  {
+        char      reserved1[8];   /* reserved */
+        char      reserved2[8];   /* reserved  */
+        char      reserved3[8];   /* reserved  */
+};
+
+/*------------------------------------------------------*/
+/*     Data buffer for CLAW                             */
+/*------------------------------------------------------*/
+struct clawbuf  {
+       char      buffer[MAX_ENVELOPE_SIZE];   /* data buffer */
+};
+
+/*------------------------------------------------------*/
+/*     Channel control block for read and write channel */
+/*------------------------------------------------------*/
+
+struct chbk {
+        unsigned int        devno;
+        int                 irq;
+       char                id[CLAW_ID_SIZE];
+       __u32               IO_active;
+        __u8                claw_state;
+        struct irb          *irb;
+               struct ccw_device   *cdev;  /* pointer to the channel device */
+       struct net_device   *ndev;
+        wait_queue_head_t   wait;
+        struct tasklet_struct    tasklet;
+        struct timer_list   timer;
+        unsigned long       flag_a;    /* atomic flags */
+#define CLAW_BH_ACTIVE      0
+        unsigned long       flag_b;    /* atomic flags */
+#define CLAW_WRITE_ACTIVE   0
+        __u8                last_dstat;
+        __u8                flag;
+       struct sk_buff_head collect_queue;
+       spinlock_t collect_lock;
+#define CLAW_WRITE      0x02      /* - Set if this is a write channel */
+#define CLAW_READ      0x01      /* - Set if this is a read channel  */
+#define CLAW_TIMER      0x80      /* - Set if timer made the wake_up  */
+};
+
+/*--------------------------------------------------------------*
+*           CLAW  environment block                             *
+*---------------------------------------------------------------*/
+
+struct claw_env {
+        unsigned int            devno[2];       /* device number */
+        char                    host_name[9];   /* Host name */
+        char                    adapter_name [9]; /* adapter name */
+        char                    api_type[9];    /* TCPIP, API or PACKED */
+        void                    *p_priv;        /* privptr */
+        __u16                   read_buffers;   /* read buffer number */
+        __u16                   write_buffers;  /* write buffer number */
+        __u16                   read_size;      /* read buffer size */
+        __u16                   write_size;     /* write buffer size */
+        __u16                   dev_id;         /* device ident */
+       __u8                    packing;        /* are we packing? */
+       volatile __u8           queme_switch;   /* gate for imed packing  */
+       volatile unsigned long  pk_delay;       /* Delay for adaptive packing */
+        __u8                    in_use;         /* device active flag */
+        struct net_device       *ndev;         /* backward ptr to the net dev*/
+};
+
+/*--------------------------------------------------------------*
+*           CLAW  main control block                            *
+*---------------------------------------------------------------*/
+
+struct claw_privbk {
+        void *p_buff_ccw;
+        __u32      p_buff_ccw_num;
+        void  *p_buff_read;
+        __u32      p_buff_read_num;
+        __u32      p_buff_pages_perread;
+        void  *p_buff_write;
+        __u32      p_buff_write_num;
+        __u32      p_buff_pages_perwrite;
+        long       active_link_ID;           /* Active logical link ID */
+        struct ccwbk *p_write_free_chain;     /* pointer to free ccw chain */
+        struct ccwbk *p_write_active_first;   /* ptr to the first write ccw */
+        struct ccwbk *p_write_active_last;    /* ptr to the last write ccw */
+        struct ccwbk *p_read_active_first;    /* ptr to the first read ccw */
+        struct ccwbk *p_read_active_last;     /* ptr to the last read ccw */
+        struct endccw *p_end_ccw;              /*ptr to ending ccw */
+        struct ccwbk *p_claw_signal_blk;      /* ptr to signal block */
+        __u32      write_free_count;       /* number of free bufs for write */
+       struct     net_device_stats  stats; /*   device status    */
+        struct chbk channel[2];            /* Channel control blocks */
+        __u8       mtc_skipping;
+        int        mtc_offset;
+        int        mtc_logical_link;
+        void       *p_mtc_envelope;
+       struct     sk_buff      *pk_skb;        /* packing buffer    */
+       int        pk_cnt;
+        struct clawctl ctl_bk;
+        struct claw_env *p_env;
+        __u8       system_validate_comp;
+        __u8       release_pend;
+        __u8      checksum_received_ip_pkts;
+       __u8      buffs_alloc;
+        struct endccw  end_ccw;
+        unsigned long  tbusy;
+
+};
+
+
+/************************************************************/
+/* define global constants                                  */
+/************************************************************/
+
+#define CCWBK_SIZE sizeof(struct ccwbk)
+
+
diff --git a/drivers/s390/net/ctcmain.h b/drivers/s390/net/ctcmain.h
new file mode 100644 (file)
index 0000000..ba3605f
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * $Id: ctcmain.h,v 1.4 2005/03/24 09:04:17 mschwide Exp $
+ *
+ * CTC / ESCON network driver
+ *
+ * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
+             Peter Tiedemann (ptiedem@de.ibm.com)
+ *
+ *
+ * Documentation used:
+ *  - Principles of Operation (IBM doc#: SA22-7201-06)
+ *  - Common IO/-Device Commands and Self Description (IBM doc#: SA22-7204-02)
+ *  - Common IO/-Device Commands and Self Description (IBM doc#: SN22-5535)
+ *  - ESCON Channel-to-Channel Adapter (IBM doc#: SA22-7203-00)
+ *  - ESCON I/O Interface (IBM doc#: SA22-7202-029
+ *
+ * This program is free software; you can redistribute 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.
+ *
+ * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.4 $
+ *
+ */
+
+#ifndef _CTCMAIN_H_
+#define _CTCMAIN_H_
+
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+
+#include "ctctty.h"
+#include "fsm.h"
+#include "cu3088.h"
+
+
+/**
+ * CCW commands, used in this driver.
+ */
+#define CCW_CMD_WRITE          0x01
+#define CCW_CMD_READ           0x02
+#define CCW_CMD_SET_EXTENDED   0xc3
+#define CCW_CMD_PREPARE                0xe3
+
+#define CTC_PROTO_S390          0
+#define CTC_PROTO_LINUX         1
+#define CTC_PROTO_LINUX_TTY     2
+#define CTC_PROTO_OS390         3
+#define CTC_PROTO_MAX           3
+
+#define CTC_BUFSIZE_LIMIT       65535
+#define CTC_BUFSIZE_DEFAULT     32768
+
+#define CTC_TIMEOUT_5SEC        5000
+
+#define CTC_INITIAL_BLOCKLEN    2
+
+#define READ                   0
+#define WRITE                  1
+
+#define CTC_ID_SIZE             BUS_ID_SIZE+3
+
+
+struct ctc_profile {
+       unsigned long maxmulti;
+       unsigned long maxcqueue;
+       unsigned long doios_single;
+       unsigned long doios_multi;
+       unsigned long txlen;
+       unsigned long tx_time;
+       struct timespec send_stamp;
+};
+
+/**
+ * Definition of one channel
+ */
+struct channel {
+
+       /**
+        * Pointer to next channel in list.
+        */
+       struct channel *next;
+       char id[CTC_ID_SIZE];
+       struct ccw_device *cdev;
+
+       /**
+        * Type of this channel.
+        * CTC/A or Escon for valid channels.
+        */
+       enum channel_types type;
+
+       /**
+        * Misc. flags. See CHANNEL_FLAGS_... below
+        */
+       __u32 flags;
+
+       /**
+        * The protocol of this channel
+        */
+       __u16 protocol;
+
+       /**
+        * I/O and irq related stuff
+        */
+       struct ccw1 *ccw;
+       struct irb *irb;
+
+       /**
+        * RX/TX buffer size
+        */
+       int max_bufsize;
+
+       /**
+        * Transmit/Receive buffer.
+        */
+       struct sk_buff *trans_skb;
+
+       /**
+        * Universal I/O queue.
+        */
+       struct sk_buff_head io_queue;
+
+       /**
+        * TX queue for collecting skb's during busy.
+        */
+       struct sk_buff_head collect_queue;
+
+       /**
+        * Amount of data in collect_queue.
+        */
+       int collect_len;
+
+       /**
+        * spinlock for collect_queue and collect_len
+        */
+       spinlock_t collect_lock;
+
+       /**
+        * Timer for detecting unresposive
+        * I/O operations.
+        */
+       fsm_timer timer;
+
+       /**
+        * Retry counter for misc. operations.
+        */
+       int retry;
+
+       /**
+        * The finite state machine of this channel
+        */
+       fsm_instance *fsm;
+
+       /**
+        * The corresponding net_device this channel
+        * belongs to.
+        */
+       struct net_device *netdev;
+
+       struct ctc_profile prof;
+
+       unsigned char *trans_skb_data;
+
+       __u16 logflags;
+};
+
+#define CHANNEL_FLAGS_READ            0
+#define CHANNEL_FLAGS_WRITE           1
+#define CHANNEL_FLAGS_INUSE           2
+#define CHANNEL_FLAGS_BUFSIZE_CHANGED 4
+#define CHANNEL_FLAGS_FAILED          8
+#define CHANNEL_FLAGS_WAITIRQ        16
+#define CHANNEL_FLAGS_RWMASK 1
+#define CHANNEL_DIRECTION(f) (f & CHANNEL_FLAGS_RWMASK)
+
+#define LOG_FLAG_ILLEGALPKT  1
+#define LOG_FLAG_ILLEGALSIZE 2
+#define LOG_FLAG_OVERRUN     4
+#define LOG_FLAG_NOMEM       8
+
+#define CTC_LOGLEVEL_INFO     1
+#define CTC_LOGLEVEL_NOTICE   2
+#define CTC_LOGLEVEL_WARN     4
+#define CTC_LOGLEVEL_EMERG    8
+#define CTC_LOGLEVEL_ERR     16
+#define CTC_LOGLEVEL_DEBUG   32
+#define CTC_LOGLEVEL_CRIT    64
+
+#define CTC_LOGLEVEL_DEFAULT \
+(CTC_LOGLEVEL_INFO | CTC_LOGLEVEL_NOTICE | CTC_LOGLEVEL_WARN | CTC_LOGLEVEL_CRIT)
+
+#define CTC_LOGLEVEL_MAX     ((CTC_LOGLEVEL_CRIT<<1)-1)
+
+#define ctc_pr_debug(fmt, arg...) \
+do { if (loglevel & CTC_LOGLEVEL_DEBUG) printk(KERN_DEBUG fmt,##arg); } while (0)
+
+#define ctc_pr_info(fmt, arg...) \
+do { if (loglevel & CTC_LOGLEVEL_INFO) printk(KERN_INFO fmt,##arg); } while (0)
+
+#define ctc_pr_notice(fmt, arg...) \
+do { if (loglevel & CTC_LOGLEVEL_NOTICE) printk(KERN_NOTICE fmt,##arg); } while (0)
+
+#define ctc_pr_warn(fmt, arg...) \
+do { if (loglevel & CTC_LOGLEVEL_WARN) printk(KERN_WARNING fmt,##arg); } while (0)
+
+#define ctc_pr_emerg(fmt, arg...) \
+do { if (loglevel & CTC_LOGLEVEL_EMERG) printk(KERN_EMERG fmt,##arg); } while (0)
+
+#define ctc_pr_err(fmt, arg...) \
+do { if (loglevel & CTC_LOGLEVEL_ERR) printk(KERN_ERR fmt,##arg); } while (0)
+
+#define ctc_pr_crit(fmt, arg...) \
+do { if (loglevel & CTC_LOGLEVEL_CRIT) printk(KERN_CRIT fmt,##arg); } while (0)
+
+struct ctc_priv {
+       struct net_device_stats stats;
+       unsigned long tbusy;
+       /**
+        * The finite state machine of this interface.
+        */
+       fsm_instance *fsm;
+       /**
+        * The protocol of this device
+        */
+       __u16 protocol;
+       /**
+        * Timer for restarting after I/O Errors
+        */
+       fsm_timer               restart_timer;
+
+       int buffer_size;
+
+       struct channel *channel[2];
+};
+
+/**
+ * Definition of our link level header.
+ */
+struct ll_header {
+       __u16 length;
+       __u16 type;
+       __u16 unused;
+};
+#define LL_HEADER_LENGTH (sizeof(struct ll_header))
+
+/**
+ * Compatibility macros for busy handling
+ * of network devices.
+ */
+static __inline__ void
+ctc_clear_busy(struct net_device * dev)
+{
+       clear_bit(0, &(((struct ctc_priv *) dev->priv)->tbusy));
+       if (((struct ctc_priv *)dev->priv)->protocol != CTC_PROTO_LINUX_TTY)
+               netif_wake_queue(dev);
+}
+
+static __inline__ int
+ctc_test_and_set_busy(struct net_device * dev)
+{
+       if (((struct ctc_priv *)dev->priv)->protocol != CTC_PROTO_LINUX_TTY)
+               netif_stop_queue(dev);
+       return test_and_set_bit(0, &((struct ctc_priv *) dev->priv)->tbusy);
+}
+
+#endif
diff --git a/drivers/s390/net/qeth_eddp.c b/drivers/s390/net/qeth_eddp.c
new file mode 100644 (file)
index 0000000..f94f1f2
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+ *
+ * linux/drivers/s390/net/qeth_eddp.c ($Revision: 1.13 $)
+ *
+ * Enhanced Device Driver Packing (EDDP) support for the qeth driver.
+ *
+ * Copyright 2004 IBM Corporation
+ *
+ *    Author(s): Thomas Spatzier <tspat@de.ibm.com>
+ *
+ *    $Revision: 1.13 $         $Date: 2005/05/04 20:19:18 $
+ *
+ */
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/ip.h>
+#include <linux/inetdevice.h>
+#include <linux/netdevice.h>
+#include <linux/kernel.h>
+#include <linux/tcp.h>
+#include <net/tcp.h>
+#include <linux/skbuff.h>
+
+#include <net/ip.h>
+
+#include "qeth.h"
+#include "qeth_mpc.h"
+#include "qeth_eddp.h"
+
+int
+qeth_eddp_check_buffers_for_context(struct qeth_qdio_out_q *queue,
+                                   struct qeth_eddp_context *ctx)
+{
+       int index = queue->next_buf_to_fill;
+       int elements_needed = ctx->num_elements;
+       int elements_in_buffer;
+       int skbs_in_buffer;
+       int buffers_needed = 0;
+
+       QETH_DBF_TEXT(trace, 5, "eddpcbfc");
+       while(elements_needed > 0) {
+               buffers_needed++;
+               if (atomic_read(&queue->bufs[index].state) !=
+                               QETH_QDIO_BUF_EMPTY)
+                       return -EBUSY;
+
+               elements_in_buffer = QETH_MAX_BUFFER_ELEMENTS(queue->card) -
+                                    queue->bufs[index].next_element_to_fill;
+               skbs_in_buffer = elements_in_buffer / ctx->elements_per_skb;
+               elements_needed -= skbs_in_buffer * ctx->elements_per_skb;
+               index = (index + 1) % QDIO_MAX_BUFFERS_PER_Q;
+       }
+       return buffers_needed;
+}
+
+static inline void
+qeth_eddp_free_context(struct qeth_eddp_context *ctx)
+{
+       int i;
+
+       QETH_DBF_TEXT(trace, 5, "eddpfctx");
+       for (i = 0; i < ctx->num_pages; ++i)
+               free_page((unsigned long)ctx->pages[i]);
+       kfree(ctx->pages);
+       if (ctx->elements != NULL)
+               kfree(ctx->elements);
+       kfree(ctx);
+}
+
+
+static inline void
+qeth_eddp_get_context(struct qeth_eddp_context *ctx)
+{
+       atomic_inc(&ctx->refcnt);
+}
+
+void
+qeth_eddp_put_context(struct qeth_eddp_context *ctx)
+{
+       if (atomic_dec_return(&ctx->refcnt) == 0)
+               qeth_eddp_free_context(ctx);
+}
+
+void
+qeth_eddp_buf_release_contexts(struct qeth_qdio_out_buffer *buf)
+{
+       struct qeth_eddp_context_reference *ref;
+       
+       QETH_DBF_TEXT(trace, 6, "eddprctx");
+       while (!list_empty(&buf->ctx_list)){
+               ref = list_entry(buf->ctx_list.next,
+                                struct qeth_eddp_context_reference, list);
+               qeth_eddp_put_context(ref->ctx);
+               list_del(&ref->list);
+               kfree(ref);
+       }
+}
+
+static inline int
+qeth_eddp_buf_ref_context(struct qeth_qdio_out_buffer *buf,
+                         struct qeth_eddp_context *ctx)
+{
+       struct qeth_eddp_context_reference *ref;
+
+       QETH_DBF_TEXT(trace, 6, "eddprfcx");
+       ref = kmalloc(sizeof(struct qeth_eddp_context_reference), GFP_ATOMIC);
+       if (ref == NULL)
+               return -ENOMEM;
+       qeth_eddp_get_context(ctx);
+       ref->ctx = ctx;
+       list_add_tail(&ref->list, &buf->ctx_list);
+       return 0;
+}
+
+int
+qeth_eddp_fill_buffer(struct qeth_qdio_out_q *queue,
+                     struct qeth_eddp_context *ctx,
+                     int index)
+{
+       struct qeth_qdio_out_buffer *buf = NULL;
+       struct qdio_buffer *buffer;
+       int elements = ctx->num_elements;
+       int element = 0;
+       int flush_cnt = 0;
+       int must_refcnt = 1;
+       int i;
+
+       QETH_DBF_TEXT(trace, 5, "eddpfibu");
+       while (elements > 0) {
+               buf = &queue->bufs[index];
+               if (atomic_read(&buf->state) != QETH_QDIO_BUF_EMPTY){
+                       /* normally this should not happen since we checked for
+                        * available elements in qeth_check_elements_for_context
+                        */
+                       if (element == 0)
+                               return -EBUSY;
+                       else {
+                               PRINT_WARN("could only partially fill eddp "
+                                          "buffer!\n");
+                               goto out;
+                       }
+               }               
+               /* check if the whole next skb fits into current buffer */
+               if ((QETH_MAX_BUFFER_ELEMENTS(queue->card) -
+                                       buf->next_element_to_fill)
+                               < ctx->elements_per_skb){
+                       /* no -> go to next buffer */
+                       atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
+                       index = (index + 1) % QDIO_MAX_BUFFERS_PER_Q;
+                       flush_cnt++;
+                       /* new buffer, so we have to add ctx to buffer'ctx_list
+                        * and increment ctx's refcnt */
+                       must_refcnt = 1;
+                       continue;
+               }       
+               if (must_refcnt){
+                       must_refcnt = 0;
+                       if (qeth_eddp_buf_ref_context(buf, ctx)){
+                               PRINT_WARN("no memory to create eddp context "
+                                          "reference\n");
+                               goto out_check;
+                       }
+               }
+               buffer = buf->buffer;
+               /* fill one skb into buffer */
+               for (i = 0; i < ctx->elements_per_skb; ++i){
+                       buffer->element[buf->next_element_to_fill].addr =
+                               ctx->elements[element].addr;
+                       buffer->element[buf->next_element_to_fill].length =
+                               ctx->elements[element].length;
+                       buffer->element[buf->next_element_to_fill].flags =
+                               ctx->elements[element].flags;
+                       buf->next_element_to_fill++;
+                       element++;
+                       elements--;
+               }
+       }
+out_check:
+       if (!queue->do_pack) {
+               QETH_DBF_TEXT(trace, 6, "fillbfnp");
+               /* set state to PRIMED -> will be flushed */
+               if (buf->next_element_to_fill > 0){
+                       atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
+                       flush_cnt++;
+               }
+       } else {
+#ifdef CONFIG_QETH_PERF_STATS
+               queue->card->perf_stats.skbs_sent_pack++;
+#endif
+               QETH_DBF_TEXT(trace, 6, "fillbfpa");
+               if (buf->next_element_to_fill >=
+                               QETH_MAX_BUFFER_ELEMENTS(queue->card)) {
+                       /*
+                        * packed buffer if full -> set state PRIMED
+                        * -> will be flushed
+                        */
+                       atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
+                       flush_cnt++;
+               }
+       }
+out:
+       return flush_cnt;
+}
+
+static inline void
+qeth_eddp_create_segment_hdrs(struct qeth_eddp_context *ctx,
+                             struct qeth_eddp_data *eddp, int data_len)
+{
+       u8 *page;
+       int page_remainder;
+       int page_offset;
+       int pkt_len;
+       struct qeth_eddp_element *element;
+
+       QETH_DBF_TEXT(trace, 5, "eddpcrsh");
+       page = ctx->pages[ctx->offset >> PAGE_SHIFT];
+       page_offset = ctx->offset % PAGE_SIZE;
+       element = &ctx->elements[ctx->num_elements];
+       pkt_len = eddp->nhl + eddp->thl + data_len;
+       /* FIXME: layer2 and VLAN !!! */
+       if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2)
+               pkt_len += ETH_HLEN;
+       if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q))
+               pkt_len += VLAN_HLEN;
+       /* does complete packet fit in current page ? */
+       page_remainder = PAGE_SIZE - page_offset;
+       if (page_remainder < (sizeof(struct qeth_hdr) + pkt_len)){
+               /* no -> go to start of next page */
+               ctx->offset += page_remainder;
+               page = ctx->pages[ctx->offset >> PAGE_SHIFT];
+               page_offset = 0;
+       }
+       memcpy(page + page_offset, &eddp->qh, sizeof(struct qeth_hdr));
+       element->addr = page + page_offset;
+       element->length = sizeof(struct qeth_hdr);
+       ctx->offset += sizeof(struct qeth_hdr);
+       page_offset += sizeof(struct qeth_hdr);
+       /* add mac header (?) */
+       if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2){
+               memcpy(page + page_offset, &eddp->mac, ETH_HLEN);
+               element->length += ETH_HLEN;
+               ctx->offset += ETH_HLEN;
+               page_offset += ETH_HLEN;
+       }
+       /* add VLAN tag */
+       if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)){
+               memcpy(page + page_offset, &eddp->vlan, VLAN_HLEN);
+               element->length += VLAN_HLEN;
+               ctx->offset += VLAN_HLEN;
+               page_offset += VLAN_HLEN;
+       }
+       /* add network header */
+       memcpy(page + page_offset, (u8 *)&eddp->nh, eddp->nhl);
+       element->length += eddp->nhl;
+       eddp->nh_in_ctx = page + page_offset;
+       ctx->offset += eddp->nhl;
+       page_offset += eddp->nhl;
+       /* add transport header */
+       memcpy(page + page_offset, (u8 *)&eddp->th, eddp->thl);
+       element->length += eddp->thl;
+       eddp->th_in_ctx = page + page_offset;
+       ctx->offset += eddp->thl;
+}
+
+static inline void
+qeth_eddp_copy_data_tcp(char *dst, struct qeth_eddp_data *eddp, int len,
+                       u32 *hcsum)
+{
+       struct skb_frag_struct *frag;
+       int left_in_frag;
+       int copy_len;
+       u8 *src;
+       
+       QETH_DBF_TEXT(trace, 5, "eddpcdtc");
+       if (skb_shinfo(eddp->skb)->nr_frags == 0) {
+               memcpy(dst, eddp->skb->data + eddp->skb_offset, len);
+               *hcsum = csum_partial(eddp->skb->data + eddp->skb_offset, len,
+                                     *hcsum);
+               eddp->skb_offset += len;
+       } else {
+               while (len > 0) {
+                       if (eddp->frag < 0) {
+                               /* we're in skb->data */
+                               left_in_frag = (eddp->skb->len - eddp->skb->data_len)
+                                               - eddp->skb_offset;
+                               src = eddp->skb->data + eddp->skb_offset;
+                       } else {
+                               frag = &skb_shinfo(eddp->skb)->
+                                       frags[eddp->frag];
+                               left_in_frag = frag->size - eddp->frag_offset;
+                               src = (u8 *)(
+                                       (page_to_pfn(frag->page) << PAGE_SHIFT)+
+                                       frag->page_offset + eddp->frag_offset);
+                       }
+                       if (left_in_frag <= 0) {
+                               eddp->frag++;
+                               eddp->frag_offset = 0;
+                               continue;
+                       }
+                       copy_len = min(left_in_frag, len);
+                       memcpy(dst, src, copy_len);
+                       *hcsum = csum_partial(src, copy_len, *hcsum);
+                       dst += copy_len;
+                       eddp->frag_offset += copy_len;
+                       eddp->skb_offset += copy_len;
+                       len -= copy_len;
+               }
+       }
+}
+
+static inline void
+qeth_eddp_create_segment_data_tcp(struct qeth_eddp_context *ctx,
+                                 struct qeth_eddp_data *eddp, int data_len,
+                                 u32 hcsum)
+{
+       u8 *page;
+       int page_remainder;
+       int page_offset;
+       struct qeth_eddp_element *element;
+       int first_lap = 1;
+
+       QETH_DBF_TEXT(trace, 5, "eddpcsdt");
+       page = ctx->pages[ctx->offset >> PAGE_SHIFT];
+       page_offset = ctx->offset % PAGE_SIZE;
+       element = &ctx->elements[ctx->num_elements];
+       while (data_len){
+               page_remainder = PAGE_SIZE - page_offset;
+               if (page_remainder < data_len){
+                       qeth_eddp_copy_data_tcp(page + page_offset, eddp,
+                                               page_remainder, &hcsum);
+                       element->length += page_remainder;
+                       if (first_lap)
+                               element->flags = SBAL_FLAGS_FIRST_FRAG;
+                       else
+                               element->flags = SBAL_FLAGS_MIDDLE_FRAG;
+                       ctx->num_elements++;
+                       element++;
+                       data_len -= page_remainder;
+                       ctx->offset += page_remainder;
+                       page = ctx->pages[ctx->offset >> PAGE_SHIFT];
+                       page_offset = 0;
+                       element->addr = page + page_offset;
+               } else {
+                       qeth_eddp_copy_data_tcp(page + page_offset, eddp,
+                                               data_len, &hcsum);
+                       element->length += data_len;
+                       if (!first_lap)
+                               element->flags = SBAL_FLAGS_LAST_FRAG;
+                       ctx->num_elements++;
+                       ctx->offset += data_len;
+                       data_len = 0;
+               }
+               first_lap = 0;
+       }
+       ((struct tcphdr *)eddp->th_in_ctx)->check = csum_fold(hcsum);
+}
+
+static inline u32
+qeth_eddp_check_tcp4_hdr(struct qeth_eddp_data *eddp, int data_len)
+{
+       u32 phcsum; /* pseudo header checksum */
+
+       QETH_DBF_TEXT(trace, 5, "eddpckt4");
+       eddp->th.tcp.h.check = 0;
+       /* compute pseudo header checksum */
+       phcsum = csum_tcpudp_nofold(eddp->nh.ip4.h.saddr, eddp->nh.ip4.h.daddr,
+                                   eddp->thl + data_len, IPPROTO_TCP, 0);
+       /* compute checksum of tcp header */
+       return csum_partial((u8 *)&eddp->th, eddp->thl, phcsum);
+}
+
+static inline u32
+qeth_eddp_check_tcp6_hdr(struct qeth_eddp_data *eddp, int data_len)
+{
+       u32 proto;
+       u32 phcsum; /* pseudo header checksum */
+
+       QETH_DBF_TEXT(trace, 5, "eddpckt6");
+       eddp->th.tcp.h.check = 0;
+       /* compute pseudo header checksum */
+       phcsum = csum_partial((u8 *)&eddp->nh.ip6.h.saddr,
+                             sizeof(struct in6_addr), 0);
+       phcsum = csum_partial((u8 *)&eddp->nh.ip6.h.daddr,
+                             sizeof(struct in6_addr), phcsum);
+       proto = htonl(IPPROTO_TCP);
+       phcsum = csum_partial((u8 *)&proto, sizeof(u32), phcsum);
+       return phcsum;
+}
+
+static inline struct qeth_eddp_data *
+qeth_eddp_create_eddp_data(struct qeth_hdr *qh, u8 *nh, u8 nhl, u8 *th, u8 thl)
+{
+       struct qeth_eddp_data *eddp;
+
+       QETH_DBF_TEXT(trace, 5, "eddpcrda");
+       eddp = kmalloc(sizeof(struct qeth_eddp_data), GFP_ATOMIC);
+       if (eddp){
+               memset(eddp, 0, sizeof(struct qeth_eddp_data));
+               eddp->nhl = nhl;
+               eddp->thl = thl;
+               memcpy(&eddp->qh, qh, sizeof(struct qeth_hdr));
+               memcpy(&eddp->nh, nh, nhl);
+               memcpy(&eddp->th, th, thl);
+               eddp->frag = -1; /* initially we're in skb->data */
+       }
+       return eddp;
+}
+
+static inline void
+__qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx,
+                            struct qeth_eddp_data *eddp)
+{
+       struct tcphdr *tcph;
+       int data_len;
+       u32 hcsum;
+       
+       QETH_DBF_TEXT(trace, 5, "eddpftcp");
+       eddp->skb_offset = sizeof(struct qeth_hdr) + eddp->nhl + eddp->thl;
+       tcph = eddp->skb->h.th;
+       while (eddp->skb_offset < eddp->skb->len) {
+               data_len = min((int)skb_shinfo(eddp->skb)->tso_size,
+                              (int)(eddp->skb->len - eddp->skb_offset));
+               /* prepare qdio hdr */
+               if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2){
+                       eddp->qh.hdr.l2.pkt_length = data_len + ETH_HLEN +
+                                                    eddp->nhl + eddp->thl -
+                                                    sizeof(struct qeth_hdr);
+#ifdef CONFIG_QETH_VLAN
+                       if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q))
+                               eddp->qh.hdr.l2.pkt_length += VLAN_HLEN;
+#endif /* CONFIG_QETH_VLAN */
+               } else
+                       eddp->qh.hdr.l3.length = data_len + eddp->nhl +
+                                                eddp->thl;
+               /* prepare ip hdr */
+               if (eddp->skb->protocol == ETH_P_IP){
+                       eddp->nh.ip4.h.tot_len = data_len + eddp->nhl +
+                                                eddp->thl;
+                       eddp->nh.ip4.h.check = 0;
+                       eddp->nh.ip4.h.check =
+                               ip_fast_csum((u8 *)&eddp->nh.ip4.h,
+                                               eddp->nh.ip4.h.ihl);
+               } else
+                       eddp->nh.ip6.h.payload_len = data_len + eddp->thl;
+               /* prepare tcp hdr */
+               if (data_len == (eddp->skb->len - eddp->skb_offset)){
+                       /* last segment -> set FIN and PSH flags */
+                       eddp->th.tcp.h.fin = tcph->fin;
+                       eddp->th.tcp.h.psh = tcph->psh;
+               }
+               if (eddp->skb->protocol == ETH_P_IP)
+                       hcsum = qeth_eddp_check_tcp4_hdr(eddp, data_len);
+               else
+                       hcsum = qeth_eddp_check_tcp6_hdr(eddp, data_len);
+               /* fill the next segment into the context */
+               qeth_eddp_create_segment_hdrs(ctx, eddp, data_len);
+               qeth_eddp_create_segment_data_tcp(ctx, eddp, data_len, hcsum);
+               if (eddp->skb_offset >= eddp->skb->len)
+                       break;
+               /* prepare headers for next round */
+               if (eddp->skb->protocol == ETH_P_IP)
+                       eddp->nh.ip4.h.id++;
+               eddp->th.tcp.h.seq += data_len;
+       }
+}
+                          
+static inline int
+qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx,
+                          struct sk_buff *skb, struct qeth_hdr *qhdr)
+{
+       struct qeth_eddp_data *eddp = NULL;
+       
+       QETH_DBF_TEXT(trace, 5, "eddpficx");
+       /* create our segmentation headers and copy original headers */
+       if (skb->protocol == ETH_P_IP)
+               eddp = qeth_eddp_create_eddp_data(qhdr, (u8 *)skb->nh.iph,
+                               skb->nh.iph->ihl*4,
+                               (u8 *)skb->h.th, skb->h.th->doff*4);
+       else
+               eddp = qeth_eddp_create_eddp_data(qhdr, (u8 *)skb->nh.ipv6h,
+                               sizeof(struct ipv6hdr),
+                               (u8 *)skb->h.th, skb->h.th->doff*4);
+
+       if (eddp == NULL) {
+               QETH_DBF_TEXT(trace, 2, "eddpfcnm");
+               return -ENOMEM;
+       }
+       if (qhdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2) {
+               memcpy(&eddp->mac, eth_hdr(skb), ETH_HLEN);
+#ifdef CONFIG_QETH_VLAN
+               if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)) {
+                       eddp->vlan[0] = __constant_htons(skb->protocol);
+                       eddp->vlan[1] = htons(vlan_tx_tag_get(skb));
+               }
+#endif /* CONFIG_QETH_VLAN */
+       }
+       /* the next flags will only be set on the last segment */
+       eddp->th.tcp.h.fin = 0;
+       eddp->th.tcp.h.psh = 0;
+       eddp->skb = skb;
+       /* begin segmentation and fill context */
+       __qeth_eddp_fill_context_tcp(ctx, eddp);
+       kfree(eddp);
+       return 0;
+}
+
+static inline void
+qeth_eddp_calc_num_pages(struct qeth_eddp_context *ctx, struct sk_buff *skb,
+                        int hdr_len)
+{
+       int skbs_per_page;
+       
+       QETH_DBF_TEXT(trace, 5, "eddpcanp");
+       /* can we put multiple skbs in one page? */
+       skbs_per_page = PAGE_SIZE / (skb_shinfo(skb)->tso_size + hdr_len);
+       if (skbs_per_page > 1){
+               ctx->num_pages = (skb_shinfo(skb)->tso_segs + 1) /
+                                skbs_per_page + 1;
+               ctx->elements_per_skb = 1;
+       } else {
+               /* no -> how many elements per skb? */
+               ctx->elements_per_skb = (skb_shinfo(skb)->tso_size + hdr_len +
+                                    PAGE_SIZE) >> PAGE_SHIFT;
+               ctx->num_pages = ctx->elements_per_skb *
+                                (skb_shinfo(skb)->tso_segs + 1);
+       }
+       ctx->num_elements = ctx->elements_per_skb *
+                           (skb_shinfo(skb)->tso_segs + 1);
+}
+
+static inline struct qeth_eddp_context *
+qeth_eddp_create_context_generic(struct qeth_card *card, struct sk_buff *skb,
+                                int hdr_len)
+{
+       struct qeth_eddp_context *ctx = NULL;
+       u8 *addr;
+       int i;
+
+       QETH_DBF_TEXT(trace, 5, "creddpcg");
+       /* create the context and allocate pages */
+       ctx = kmalloc(sizeof(struct qeth_eddp_context), GFP_ATOMIC);
+       if (ctx == NULL){
+               QETH_DBF_TEXT(trace, 2, "ceddpcn1");
+               return NULL;
+       }
+       memset(ctx, 0, sizeof(struct qeth_eddp_context));
+       ctx->type = QETH_LARGE_SEND_EDDP;
+       qeth_eddp_calc_num_pages(ctx, skb, hdr_len);
+       if (ctx->elements_per_skb > QETH_MAX_BUFFER_ELEMENTS(card)){
+               QETH_DBF_TEXT(trace, 2, "ceddpcis");
+               kfree(ctx);
+               return NULL;
+       }
+       ctx->pages = kmalloc(ctx->num_pages * sizeof(u8 *), GFP_ATOMIC);
+       if (ctx->pages == NULL){
+               QETH_DBF_TEXT(trace, 2, "ceddpcn2");
+               kfree(ctx);
+               return NULL;
+       }
+       memset(ctx->pages, 0, ctx->num_pages * sizeof(u8 *));
+       for (i = 0; i < ctx->num_pages; ++i){
+               addr = (u8 *)__get_free_page(GFP_ATOMIC);
+               if (addr == NULL){
+                       QETH_DBF_TEXT(trace, 2, "ceddpcn3");
+                       ctx->num_pages = i;
+                       qeth_eddp_free_context(ctx);
+                       return NULL;
+               }
+               memset(addr, 0, PAGE_SIZE);
+               ctx->pages[i] = addr;
+       }
+       ctx->elements = kmalloc(ctx->num_elements *
+                               sizeof(struct qeth_eddp_element), GFP_ATOMIC);
+       if (ctx->elements == NULL){
+               QETH_DBF_TEXT(trace, 2, "ceddpcn4");
+               qeth_eddp_free_context(ctx);
+               return NULL;
+       }
+       memset(ctx->elements, 0,
+              ctx->num_elements * sizeof(struct qeth_eddp_element));
+       /* reset num_elements; will be incremented again in fill_buffer to
+        * reflect number of actually used elements */
+       ctx->num_elements = 0;
+       return ctx;
+}
+
+static inline struct qeth_eddp_context *
+qeth_eddp_create_context_tcp(struct qeth_card *card, struct sk_buff *skb,
+                            struct qeth_hdr *qhdr)
+{
+       struct qeth_eddp_context *ctx = NULL;
+       
+       QETH_DBF_TEXT(trace, 5, "creddpct");
+       if (skb->protocol == ETH_P_IP)
+               ctx = qeth_eddp_create_context_generic(card, skb,
+                       sizeof(struct qeth_hdr) + skb->nh.iph->ihl*4 +
+                       skb->h.th->doff*4);
+       else if (skb->protocol == ETH_P_IPV6)
+               ctx = qeth_eddp_create_context_generic(card, skb,
+                       sizeof(struct qeth_hdr) + sizeof(struct ipv6hdr) +
+                       skb->h.th->doff*4);
+       else
+               QETH_DBF_TEXT(trace, 2, "cetcpinv");
+
+       if (ctx == NULL) {
+               QETH_DBF_TEXT(trace, 2, "creddpnl");
+               return NULL;
+       }
+       if (qeth_eddp_fill_context_tcp(ctx, skb, qhdr)){
+               QETH_DBF_TEXT(trace, 2, "ceddptfe");
+               qeth_eddp_free_context(ctx);
+               return NULL;
+       }
+       atomic_set(&ctx->refcnt, 1);
+       return ctx;
+}
+
+struct qeth_eddp_context *
+qeth_eddp_create_context(struct qeth_card *card, struct sk_buff *skb,
+                        struct qeth_hdr *qhdr)
+{
+       QETH_DBF_TEXT(trace, 5, "creddpc");
+       switch (skb->sk->sk_protocol){
+       case IPPROTO_TCP:
+               return qeth_eddp_create_context_tcp(card, skb, qhdr);
+       default:
+               QETH_DBF_TEXT(trace, 2, "eddpinvp");
+       }
+       return NULL;
+}
+
+
diff --git a/drivers/s390/net/qeth_eddp.h b/drivers/s390/net/qeth_eddp.h
new file mode 100644 (file)
index 0000000..e1b5186
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * linux/drivers/s390/net/qeth_eddp.c ($Revision: 1.5 $)
+ *
+ * Header file for qeth enhanced device driver pakcing.
+ *
+ * Copyright 2004 IBM Corporation
+ *
+ *    Author(s): Thomas Spatzier <tspat@de.ibm.com>
+ *
+ *    $Revision: 1.5 $  $Date: 2005/03/24 09:04:18 $
+ *
+ */
+#ifndef __QETH_EDDP_H__
+#define __QETH_EDDP_H__
+
+struct qeth_eddp_element {
+       u32 flags;
+       u32 length;
+       void *addr;
+};
+
+struct qeth_eddp_context {
+       atomic_t refcnt;
+       enum qeth_large_send_types type;
+       int num_pages;                      /* # of allocated pages */
+       u8 **pages;                         /* pointers to pages */
+       int offset;                         /* offset in ctx during creation */
+       int num_elements;                   /* # of required 'SBALEs' */
+       struct qeth_eddp_element *elements; /* array of 'SBALEs' */
+       int elements_per_skb;               /* # of 'SBALEs' per skb **/
+};
+
+struct qeth_eddp_context_reference {
+       struct list_head list;
+       struct qeth_eddp_context *ctx;
+};
+
+extern struct qeth_eddp_context *
+qeth_eddp_create_context(struct qeth_card *,struct sk_buff *,struct qeth_hdr *);
+
+extern void
+qeth_eddp_put_context(struct qeth_eddp_context *);
+
+extern int
+qeth_eddp_fill_buffer(struct qeth_qdio_out_q *,struct qeth_eddp_context *,int);
+
+extern void
+qeth_eddp_buf_release_contexts(struct qeth_qdio_out_buffer *);
+
+extern int
+qeth_eddp_check_buffers_for_context(struct qeth_qdio_out_q *,
+                                   struct qeth_eddp_context *);
+/*
+ * Data used for fragmenting a IP packet.
+ */
+struct qeth_eddp_data {
+       struct qeth_hdr qh;
+       struct ethhdr mac;
+       u16 vlan[2];
+       union {
+               struct {
+                       struct iphdr h;
+                       u8 options[40];
+               } ip4;
+               struct {
+                       struct ipv6hdr h;
+               } ip6;
+       } nh;
+       u8 nhl;
+       void *nh_in_ctx;        /* address of nh within the ctx */
+       union {
+               struct {
+                       struct tcphdr h;
+                       u8 options[40];
+               } tcp;
+       } th;
+       u8 thl;
+       void *th_in_ctx;        /* address of th within the ctx */
+       struct sk_buff *skb;
+       int skb_offset;
+       int frag;
+       int frag_offset;
+} __attribute__ ((packed));
+
+#endif /* __QETH_EDDP_H__ */
diff --git a/drivers/s390/net/qeth_tso.h b/drivers/s390/net/qeth_tso.h
new file mode 100644 (file)
index 0000000..ad33e6f
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * linux/drivers/s390/net/qeth_tso.h ($Revision: 1.7 $)
+ *
+ * Header file for qeth TCP Segmentation Offload support.
+ *
+ * Copyright 2004 IBM Corporation
+ *
+ *    Author(s): Frank Pavlic <pavlic@de.ibm.com>
+ *
+ *    $Revision: 1.7 $  $Date: 2005/05/04 20:19:18 $
+ *
+ */
+#ifndef __QETH_TSO_H__
+#define __QETH_TSO_H__
+
+#include <linux/skbuff.h>
+#include <linux/tcp.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <net/ip6_checksum.h>
+#include "qeth.h"
+#include "qeth_mpc.h"
+
+
+static inline struct qeth_hdr_tso *
+qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb)
+{
+       QETH_DBF_TEXT(trace, 5, "tsoprsk");
+       return qeth_push_skb(card, skb, sizeof(struct qeth_hdr_tso));
+}
+
+/**
+ * fill header for a TSO packet
+ */
+static inline void
+qeth_tso_fill_header(struct qeth_card *card, struct sk_buff *skb)
+{
+       struct qeth_hdr_tso *hdr;
+       struct tcphdr *tcph;
+       struct iphdr *iph;
+
+       QETH_DBF_TEXT(trace, 5, "tsofhdr");
+
+       hdr  = (struct qeth_hdr_tso *) skb->data;
+       iph  = skb->nh.iph;
+       tcph = skb->h.th;
+       /*fix header to TSO values ...*/
+       hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO;
+       /*set values which are fix for the first approach ...*/
+       hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso);
+       hdr->ext.imb_hdr_no  = 1;
+       hdr->ext.hdr_type    = 1;
+       hdr->ext.hdr_version = 1;
+       hdr->ext.hdr_len     = 28;
+       /*insert non-fix values */
+       hdr->ext.mss = skb_shinfo(skb)->tso_size;
+       hdr->ext.dg_hdr_len = (__u16)(iph->ihl*4 + tcph->doff*4);
+       hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len -
+                                      sizeof(struct qeth_hdr_tso));
+}
+
+/**
+ * change some header values as requested by hardware
+ */
+static inline void
+qeth_tso_set_tcpip_header(struct qeth_card *card, struct sk_buff *skb)
+{
+       struct iphdr *iph;
+       struct ipv6hdr *ip6h;
+       struct tcphdr *tcph;
+
+       iph  = skb->nh.iph;
+       ip6h = skb->nh.ipv6h;
+       tcph = skb->h.th;
+
+       tcph->check = 0;
+       if (skb->protocol == ETH_P_IPV6) {
+               ip6h->payload_len = 0;
+               tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
+                                              0, IPPROTO_TCP, 0);
+               return;
+       }
+       /*OSA want us to set these values ...*/
+       tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
+                                        0, IPPROTO_TCP, 0);
+       iph->tot_len = 0;
+       iph->check = 0;
+}
+
+static inline int
+qeth_tso_prepare_packet(struct qeth_card *card, struct sk_buff *skb,
+                       int ipv, int cast_type)
+{
+       struct qeth_hdr_tso *hdr;
+
+       QETH_DBF_TEXT(trace, 5, "tsoprep");
+
+       hdr = (struct qeth_hdr_tso *) qeth_tso_prepare_skb(card, &skb);
+       if (hdr == NULL) {
+               QETH_DBF_TEXT(trace, 4, "tsoperr");
+               return -ENOMEM;
+       }
+       memset(hdr, 0, sizeof(struct qeth_hdr_tso));
+       /*fill first 32 bytes of  qdio header as used
+        *FIXME: TSO has two struct members
+        * with different names but same size
+        * */
+       qeth_fill_header(card, &hdr->hdr, skb, ipv, cast_type);
+       qeth_tso_fill_header(card, skb);
+       qeth_tso_set_tcpip_header(card, skb);
+       return 0;
+}
+
+static inline void
+__qeth_fill_buffer_frag(struct sk_buff *skb, struct qdio_buffer *buffer,
+                       int is_tso, int *next_element_to_fill)
+{
+       struct skb_frag_struct *frag;
+       int fragno;
+       unsigned long addr;
+       int element, cnt, dlen;
+       
+       fragno = skb_shinfo(skb)->nr_frags;
+       element = *next_element_to_fill;
+       dlen = 0;
+       
+       if (is_tso)
+               buffer->element[element].flags =
+                       SBAL_FLAGS_MIDDLE_FRAG;
+       else
+               buffer->element[element].flags =
+                       SBAL_FLAGS_FIRST_FRAG;
+       if ( (dlen = (skb->len - skb->data_len)) ) {
+               buffer->element[element].addr = skb->data;
+               buffer->element[element].length = dlen;
+               element++;
+       }
+       for (cnt = 0; cnt < fragno; cnt++) {
+               frag = &skb_shinfo(skb)->frags[cnt];
+               addr = (page_to_pfn(frag->page) << PAGE_SHIFT) +
+                       frag->page_offset;
+               buffer->element[element].addr = (char *)addr;
+               buffer->element[element].length = frag->size;
+               if (cnt < (fragno - 1))
+                       buffer->element[element].flags =
+                               SBAL_FLAGS_MIDDLE_FRAG;
+               else
+                       buffer->element[element].flags =
+                               SBAL_FLAGS_LAST_FRAG;
+               element++;
+       }
+       *next_element_to_fill = element;
+}
+#endif /* __QETH_TSO_H__ */
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
new file mode 100644 (file)
index 0000000..1276bd7
--- /dev/null
@@ -0,0 +1,1291 @@
+/*******************************************************************
+ * 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_attr.c 1.24 2005/04/13 11:58:55EDT sf_support Exp  $
+ */
+
+#include <linux/ctype.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_version.h"
+#include "lpfc_compat.h"
+#include "lpfc_crtn.h"
+
+
+static void
+lpfc_jedec_to_ascii(int incr, char hdw[])
+{
+       int i, j;
+       for (i = 0; i < 8; i++) {
+               j = (incr & 0xf);
+               if (j <= 9)
+                       hdw[7 - i] = 0x30 +  j;
+                else
+                       hdw[7 - i] = 0x61 + j - 10;
+               incr = (incr >> 4);
+       }
+       hdw[8] = 0;
+       return;
+}
+
+static ssize_t
+lpfc_drvr_version_show(struct class_device *cdev, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, LPFC_MODULE_DESC "\n");
+}
+
+static ssize_t
+management_version_show(struct class_device *cdev, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, DFC_API_VERSION "\n");
+}
+
+static ssize_t
+lpfc_info_show(struct class_device *cdev, char *buf)
+{
+       struct Scsi_Host *host = class_to_shost(cdev);
+       return snprintf(buf, PAGE_SIZE, "%s\n",lpfc_info(host));
+}
+
+static ssize_t
+lpfc_serialnum_show(struct class_device *cdev, char *buf)
+{
+       struct Scsi_Host *host = class_to_shost(cdev);
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+       return snprintf(buf, PAGE_SIZE, "%s\n",phba->SerialNumber);
+}
+
+static ssize_t
+lpfc_modeldesc_show(struct class_device *cdev, char *buf)
+{
+       struct Scsi_Host *host = class_to_shost(cdev);
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+       return snprintf(buf, PAGE_SIZE, "%s\n",phba->ModelDesc);
+}
+
+static ssize_t
+lpfc_modelname_show(struct class_device *cdev, char *buf)
+{
+       struct Scsi_Host *host = class_to_shost(cdev);
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+       return snprintf(buf, PAGE_SIZE, "%s\n",phba->ModelName);
+}
+
+static ssize_t
+lpfc_programtype_show(struct class_device *cdev, char *buf)
+{
+       struct Scsi_Host *host = class_to_shost(cdev);
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+       return snprintf(buf, PAGE_SIZE, "%s\n",phba->ProgramType);
+}
+
+static ssize_t
+lpfc_portnum_show(struct class_device *cdev, char *buf)
+{
+       struct Scsi_Host *host = class_to_shost(cdev);
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+       return snprintf(buf, PAGE_SIZE, "%s\n",phba->Port);
+}
+
+static ssize_t
+lpfc_fwrev_show(struct class_device *cdev, char *buf)
+{
+       struct Scsi_Host *host = class_to_shost(cdev);
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+       char fwrev[32];
+       lpfc_decode_firmware_rev(phba, fwrev, 1);
+       return snprintf(buf, PAGE_SIZE, "%s\n",fwrev);
+}
+
+static ssize_t
+lpfc_hdw_show(struct class_device *cdev, char *buf)
+{
+       char hdw[9];
+       struct Scsi_Host *host = class_to_shost(cdev);
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+       lpfc_vpd_t *vp = &phba->vpd;
+       lpfc_jedec_to_ascii(vp->rev.biuRev, hdw);
+       return snprintf(buf, PAGE_SIZE, "%s\n", hdw);
+}
+static ssize_t
+lpfc_option_rom_version_show(struct class_device *cdev, char *buf)
+{
+       struct Scsi_Host *host = class_to_shost(cdev);
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+       return snprintf(buf, PAGE_SIZE, "%s\n", phba->OptionROMVersion);
+}
+static ssize_t
+lpfc_state_show(struct class_device *cdev, char *buf)
+{
+       struct Scsi_Host *host = class_to_shost(cdev);
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+       int len = 0;
+       switch (phba->hba_state) {
+       case LPFC_INIT_START:
+       case LPFC_INIT_MBX_CMDS:
+       case LPFC_LINK_DOWN:
+               len += snprintf(buf + len, PAGE_SIZE-len, "Link Down\n");
+               break;
+       case LPFC_LINK_UP:
+       case LPFC_LOCAL_CFG_LINK:
+               len += snprintf(buf + len, PAGE_SIZE-len, "Link Up\n");
+               break;
+       case LPFC_FLOGI:
+       case LPFC_FABRIC_CFG_LINK:
+       case LPFC_NS_REG:
+       case LPFC_NS_QRY:
+       case LPFC_BUILD_DISC_LIST:
+       case LPFC_DISC_AUTH:
+       case LPFC_CLEAR_LA:
+               len += snprintf(buf + len, PAGE_SIZE-len,
+                               "Link Up - Discovery\n");
+               break;
+       case LPFC_HBA_READY:
+               len += snprintf(buf + len, PAGE_SIZE-len,
+                               "Link Up - Ready:\n");
+               if (phba->fc_topology == TOPOLOGY_LOOP) {
+                       if (phba->fc_flag & FC_PUBLIC_LOOP)
+                               len += snprintf(buf + len, PAGE_SIZE-len,
+                                               "   Public Loop\n");
+                       else
+                               len += snprintf(buf + len, PAGE_SIZE-len,
+                                               "   Private Loop\n");
+               } else {
+                       if (phba->fc_flag & FC_FABRIC)
+                               len += snprintf(buf + len, PAGE_SIZE-len,
+                                               "   Fabric\n");
+                       else
+                               len += snprintf(buf + len, PAGE_SIZE-len,
+                                               "   Point-2-Point\n");
+               }
+       }
+       return len;
+}
+
+static ssize_t
+lpfc_num_discovered_ports_show(struct class_device *cdev, char *buf)
+{
+       struct Scsi_Host *host = class_to_shost(cdev);
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+       return snprintf(buf, PAGE_SIZE, "%d\n", phba->fc_map_cnt +
+                                                       phba->fc_unmap_cnt);
+}
+
+
+static ssize_t
+lpfc_issue_lip (struct class_device *cdev, const char *buf, size_t count)
+{
+       struct Scsi_Host *host = class_to_shost(cdev);
+       struct lpfc_hba *phba = (struct lpfc_hba *) host->hostdata[0];
+       int val = 0;
+       LPFC_MBOXQ_t *pmboxq;
+       int mbxstatus = MBXERR_ERROR;
+
+       if ((sscanf(buf, "%d", &val) != 1) ||
+           (val != 1))
+               return -EINVAL;
+
+       if ((phba->fc_flag & FC_OFFLINE_MODE) ||
+           (phba->hba_state != LPFC_HBA_READY))
+               return -EPERM;
+
+       pmboxq = mempool_alloc(phba->mbox_mem_pool,GFP_KERNEL);
+
+       if (!pmboxq)
+               return -ENOMEM;
+
+       memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t));
+       lpfc_init_link(phba, pmboxq, phba->cfg_topology, phba->cfg_link_speed);
+       mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
+
+       if (mbxstatus == MBX_TIMEOUT)
+               pmboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+       else
+               mempool_free( pmboxq, phba->mbox_mem_pool);
+
+       if (mbxstatus == MBXERR_ERROR)
+               return -EIO;
+
+       return strlen(buf);
+}
+
+static ssize_t
+lpfc_nport_evt_cnt_show(struct class_device *cdev, char *buf)
+{
+       struct Scsi_Host *host = class_to_shost(cdev);
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+       return snprintf(buf, PAGE_SIZE, "%d\n", phba->nport_event_cnt);
+}
+
+static ssize_t
+lpfc_board_online_show(struct class_device *cdev, char *buf)
+{
+       struct Scsi_Host *host = class_to_shost(cdev);
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+
+       if (!phba) return 0;
+
+       if (phba->fc_flag & FC_OFFLINE_MODE)
+               return snprintf(buf, PAGE_SIZE, "0\n");
+       else
+               return snprintf(buf, PAGE_SIZE, "1\n");
+}
+
+static ssize_t
+lpfc_board_online_store(struct class_device *cdev, const char *buf,
+                                                               size_t count)
+{
+       struct Scsi_Host *host = class_to_shost(cdev);
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+       struct completion online_compl;
+       int val=0, status=0;
+
+       if (sscanf(buf, "%d", &val) != 1)
+               return 0;
+
+       init_completion(&online_compl);
+
+       if (val)
+               lpfc_workq_post_event(phba, &status, &online_compl,
+                                                       LPFC_EVT_ONLINE);
+       else
+               lpfc_workq_post_event(phba, &status, &online_compl,
+                                                       LPFC_EVT_OFFLINE);
+       wait_for_completion(&online_compl);
+       if (!status)
+               return strlen(buf);
+       else
+               return 0;
+}
+
+
+#define lpfc_param_show(attr)  \
+static ssize_t \
+lpfc_##attr##_show(struct class_device *cdev, char *buf) \
+{ \
+       struct Scsi_Host *host = class_to_shost(cdev);\
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];\
+       int val = 0;\
+       if (phba){\
+               val = phba->cfg_##attr;\
+               return snprintf(buf, PAGE_SIZE, "%d\n",\
+                               phba->cfg_##attr);\
+       }\
+       return 0;\
+}
+
+#define lpfc_param_store(attr, minval, maxval) \
+static ssize_t \
+lpfc_##attr##_store(struct class_device *cdev, const char *buf, size_t count) \
+{ \
+       struct Scsi_Host *host = class_to_shost(cdev);\
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];\
+       int val = 0;\
+       if (!isdigit(buf[0]))\
+               return -EINVAL;\
+       if (sscanf(buf, "0x%x", &val) != 1)\
+               if (sscanf(buf, "%d", &val) != 1)\
+                       return -EINVAL;\
+       if (phba){\
+               if (val >= minval && val <= maxval) {\
+                       phba->cfg_##attr = val;\
+                       return strlen(buf);\
+               }\
+       }\
+       return 0;\
+}
+
+#define LPFC_ATTR_R_NOINIT(name, desc) \
+extern int lpfc_##name;\
+module_param(lpfc_##name, int, 0);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_param_show(name)\
+static CLASS_DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
+
+#define LPFC_ATTR_R(name, defval, minval, maxval, desc) \
+static int lpfc_##name = defval;\
+module_param(lpfc_##name, int, 0);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_param_show(name)\
+static CLASS_DEVICE_ATTR(lpfc_##name, S_IRUGO , lpfc_##name##_show, NULL)
+
+#define LPFC_ATTR_RW(name, defval, minval, maxval, desc) \
+static int lpfc_##name = defval;\
+module_param(lpfc_##name, int, 0);\
+MODULE_PARM_DESC(lpfc_##name, desc);\
+lpfc_param_show(name)\
+lpfc_param_store(name, minval, maxval)\
+static CLASS_DEVICE_ATTR(lpfc_##name, S_IRUGO | S_IWUSR,\
+                        lpfc_##name##_show, lpfc_##name##_store)
+
+static CLASS_DEVICE_ATTR(info, S_IRUGO, lpfc_info_show, NULL);
+static CLASS_DEVICE_ATTR(serialnum, S_IRUGO, lpfc_serialnum_show, NULL);
+static CLASS_DEVICE_ATTR(modeldesc, S_IRUGO, lpfc_modeldesc_show, NULL);
+static CLASS_DEVICE_ATTR(modelname, S_IRUGO, lpfc_modelname_show, NULL);
+static CLASS_DEVICE_ATTR(programtype, S_IRUGO, lpfc_programtype_show, NULL);
+static CLASS_DEVICE_ATTR(portnum, S_IRUGO, lpfc_portnum_show, NULL);
+static CLASS_DEVICE_ATTR(fwrev, S_IRUGO, lpfc_fwrev_show, NULL);
+static CLASS_DEVICE_ATTR(hdw, S_IRUGO, lpfc_hdw_show, NULL);
+static CLASS_DEVICE_ATTR(state, S_IRUGO, lpfc_state_show, NULL);
+static CLASS_DEVICE_ATTR(option_rom_version, S_IRUGO,
+                                       lpfc_option_rom_version_show, NULL);
+static CLASS_DEVICE_ATTR(num_discovered_ports, S_IRUGO,
+                                       lpfc_num_discovered_ports_show, NULL);
+static CLASS_DEVICE_ATTR(nport_evt_cnt, S_IRUGO, lpfc_nport_evt_cnt_show, NULL);
+static CLASS_DEVICE_ATTR(lpfc_drvr_version, S_IRUGO, lpfc_drvr_version_show,
+                        NULL);
+static CLASS_DEVICE_ATTR(management_version, S_IRUGO, management_version_show,
+                        NULL);
+static CLASS_DEVICE_ATTR(issue_lip, S_IWUSR, NULL, lpfc_issue_lip);
+static CLASS_DEVICE_ATTR(board_online, S_IRUGO | S_IWUSR,
+                        lpfc_board_online_show, lpfc_board_online_store);
+
+
+/*
+# lpfc_log_verbose: Only turn this flag on if you are willing to risk being
+# deluged with LOTS of information.
+# You can set a bit mask to record specific types of verbose messages:
+#
+# LOG_ELS                       0x1        ELS events
+# LOG_DISCOVERY                 0x2        Link discovery events
+# LOG_MBOX                      0x4        Mailbox events
+# LOG_INIT                      0x8        Initialization events
+# LOG_LINK_EVENT                0x10       Link events
+# LOG_IP                        0x20       IP traffic history
+# LOG_FCP                       0x40       FCP traffic history
+# LOG_NODE                      0x80       Node table events
+# LOG_MISC                      0x400      Miscellaneous events
+# LOG_SLI                       0x800      SLI events
+# LOG_CHK_COND                  0x1000     FCP Check condition flag
+# LOG_LIBDFC                    0x2000     LIBDFC events
+# LOG_ALL_MSG                   0xffff     LOG all messages
+*/
+LPFC_ATTR_RW(log_verbose, 0x0, 0x0, 0xffff, "Verbose logging bit-mask");
+
+/*
+# lun_queue_depth:  This parameter is used to limit the number of outstanding
+# commands per FCP LUN. Value range is [1,128]. Default value is 30.
+*/
+LPFC_ATTR_R(lun_queue_depth, 30, 1, 128,
+           "Max number of FCP commands we can queue to a specific LUN");
+
+/*
+# Some disk devices have a "select ID" or "select Target" capability.
+# From a protocol standpoint "select ID" usually means select the
+# Fibre channel "ALPA".  In the FC-AL Profile there is an "informative
+# annex" which contains a table that maps a "select ID" (a number
+# between 0 and 7F) to an ALPA.  By default, for compatibility with
+# older drivers, the lpfc driver scans this table from low ALPA to high
+# ALPA.
+#
+# Turning on the scan-down variable (on  = 1, off = 0) will
+# cause the lpfc driver to use an inverted table, effectively
+# scanning ALPAs from high to low. Value range is [0,1]. Default value is 1.
+#
+# (Note: This "select ID" functionality is a LOOP ONLY characteristic
+# and will not work across a fabric. Also this parameter will take
+# effect only in the case when ALPA map is not available.)
+*/
+LPFC_ATTR_R(scan_down, 1, 0, 1,
+            "Start scanning for devices from highest ALPA to lowest");
+
+/*
+# lpfc_nodev_tmo: If set, it will hold all I/O errors on devices that disappear
+# until the timer expires. Value range is [0,255]. Default value is 20.
+# NOTE: this MUST be less then the SCSI Layer command timeout - 1.
+*/
+LPFC_ATTR_RW(nodev_tmo, 30, 0, 255,
+            "Seconds driver will hold I/O waiting for a device to come back");
+
+/*
+# lpfc_topology:  link topology for init link
+#            0x0  = attempt loop mode then point-to-point
+#            0x02 = attempt point-to-point mode only
+#            0x04 = attempt loop mode only
+#            0x06 = attempt point-to-point mode then loop
+# Set point-to-point mode if you want to run as an N_Port.
+# Set loop mode if you want to run as an NL_Port. Value range is [0,0x6].
+# Default value is 0.
+*/
+LPFC_ATTR_R(topology, 0, 0, 6, "Select Fibre Channel topology");
+
+/*
+# lpfc_link_speed: Link speed selection for initializing the Fibre Channel
+# connection.
+#       0  = auto select (default)
+#       1  = 1 Gigabaud
+#       2  = 2 Gigabaud
+#       4  = 4 Gigabaud
+# Value range is [0,4]. Default value is 0.
+*/
+LPFC_ATTR_R(link_speed, 0, 0, 4, "Select link speed");
+
+/*
+# lpfc_fcp_class:  Determines FC class to use for the FCP protocol.
+# Value range is [2,3]. Default value is 3.
+*/
+LPFC_ATTR_R(fcp_class, 3, 2, 3,
+            "Select Fibre Channel class of service for FCP sequences");
+
+/*
+# lpfc_use_adisc: Use ADISC for FCP rediscovery instead of PLOGI. Value range
+# is [0,1]. Default value is 0.
+*/
+LPFC_ATTR_RW(use_adisc, 0, 0, 1,
+            "Use ADISC on rediscovery to authenticate FCP devices");
+
+/*
+# lpfc_ack0: Use ACK0, instead of ACK1 for class 2 acknowledgement. Value
+# range is [0,1]. Default value is 0.
+*/
+LPFC_ATTR_R(ack0, 0, 0, 1, "Enable ACK0 support");
+
+/*
+# lpfc_cr_delay & lpfc_cr_count: Default values for I/O colaesing
+# cr_delay (msec) or cr_count outstanding commands. cr_delay can take
+# value [0,63]. cr_count can take value [0,255]. Default value of cr_delay
+# is 0. Default value of cr_count is 1. The cr_count feature is disabled if
+# cr_delay is set to 0.
+*/
+static int lpfc_cr_delay = 0;
+module_param(lpfc_cr_delay, int , 0);
+MODULE_PARM_DESC(lpfc_cr_delay, "A count of milliseconds after which an "
+               "interrupt response is generated");
+
+static int lpfc_cr_count = 1;
+module_param(lpfc_cr_count, int, 0);
+MODULE_PARM_DESC(lpfc_cr_count, "A count of I/O completions after which an "
+               "interrupt response is generated");
+
+/*
+# lpfc_fdmi_on: controls FDMI support.
+#       0 = no FDMI support
+#       1 = support FDMI without attribute of hostname
+#       2 = support FDMI with attribute of hostname
+# Value range [0,2]. Default value is 0.
+*/
+LPFC_ATTR_RW(fdmi_on, 0, 0, 2, "Enable FDMI support");
+
+/*
+# Specifies the maximum number of ELS cmds we can have outstanding (for
+# discovery). Value range is [1,64]. Default value = 32.
+*/
+static int lpfc_discovery_threads = 32;
+module_param(lpfc_discovery_threads, int, 0);
+MODULE_PARM_DESC(lpfc_discovery_threads, "Maximum number of ELS commands "
+                "during discovery");
+
+/*
+# lpfc_max_luns: maximum number of LUNs per target driver will support
+# Value range is [1,32768]. Default value is 256.
+# NOTE: The SCSI layer will scan each target for this many luns
+*/
+LPFC_ATTR_R(max_luns, 256, 1, 32768,
+            "Maximum number of LUNs per target driver will support");
+
+struct class_device_attribute *lpfc_host_attrs[] = {
+       &class_device_attr_info,
+       &class_device_attr_serialnum,
+       &class_device_attr_modeldesc,
+       &class_device_attr_modelname,
+       &class_device_attr_programtype,
+       &class_device_attr_portnum,
+       &class_device_attr_fwrev,
+       &class_device_attr_hdw,
+       &class_device_attr_option_rom_version,
+       &class_device_attr_state,
+       &class_device_attr_num_discovered_ports,
+       &class_device_attr_lpfc_drvr_version,
+       &class_device_attr_lpfc_log_verbose,
+       &class_device_attr_lpfc_lun_queue_depth,
+       &class_device_attr_lpfc_nodev_tmo,
+       &class_device_attr_lpfc_fcp_class,
+       &class_device_attr_lpfc_use_adisc,
+       &class_device_attr_lpfc_ack0,
+       &class_device_attr_lpfc_topology,
+       &class_device_attr_lpfc_scan_down,
+       &class_device_attr_lpfc_link_speed,
+       &class_device_attr_lpfc_fdmi_on,
+       &class_device_attr_lpfc_max_luns,
+       &class_device_attr_nport_evt_cnt,
+       &class_device_attr_management_version,
+       &class_device_attr_issue_lip,
+       &class_device_attr_board_online,
+       NULL,
+};
+
+static ssize_t
+sysfs_ctlreg_write(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+       size_t buf_off;
+       struct Scsi_Host *host = class_to_shost(container_of(kobj,
+                                            struct class_device, kobj));
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+
+       if ((off + count) > FF_REG_AREA_SIZE)
+               return -ERANGE;
+
+       if (count == 0) return 0;
+
+       if (off % 4 || count % 4 || (unsigned long)buf % 4)
+               return -EINVAL;
+
+       spin_lock_irq(phba->host->host_lock);
+
+       if (!(phba->fc_flag & FC_OFFLINE_MODE)) {
+               spin_unlock_irq(phba->host->host_lock);
+               return -EPERM;
+       }
+
+       for (buf_off = 0; buf_off < count; buf_off += sizeof(uint32_t))
+               writel(*((uint32_t *)(buf + buf_off)),
+                      phba->ctrl_regs_memmap_p + off + buf_off);
+
+       spin_unlock_irq(phba->host->host_lock);
+
+       return count;
+}
+
+static ssize_t
+sysfs_ctlreg_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+       size_t buf_off;
+       uint32_t * tmp_ptr;
+       struct Scsi_Host *host = class_to_shost(container_of(kobj,
+                                            struct class_device, kobj));
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+
+       if (off > FF_REG_AREA_SIZE)
+               return -ERANGE;
+
+       if ((off + count) > FF_REG_AREA_SIZE)
+               count = FF_REG_AREA_SIZE - off;
+
+       if (count == 0) return 0;
+
+       if (off % 4 || count % 4 || (unsigned long)buf % 4)
+               return -EINVAL;
+
+       spin_lock_irq(phba->host->host_lock);
+
+       for (buf_off = 0; buf_off < count; buf_off += sizeof(uint32_t)) {
+               tmp_ptr = (uint32_t *)(buf + buf_off);
+               *tmp_ptr = readl(phba->ctrl_regs_memmap_p + off + buf_off);
+       }
+
+       spin_unlock_irq(phba->host->host_lock);
+
+       return count;
+}
+
+static struct bin_attribute sysfs_ctlreg_attr = {
+       .attr = {
+               .name = "ctlreg",
+               .mode = S_IRUSR | S_IWUSR,
+               .owner = THIS_MODULE,
+       },
+       .size = 256,
+       .read = sysfs_ctlreg_read,
+       .write = sysfs_ctlreg_write,
+};
+
+
+static void
+sysfs_mbox_idle (struct lpfc_hba * phba)
+{
+       phba->sysfs_mbox.state = SMBOX_IDLE;
+       phba->sysfs_mbox.offset = 0;
+
+       if (phba->sysfs_mbox.mbox) {
+               mempool_free(phba->sysfs_mbox.mbox,
+                            phba->mbox_mem_pool);
+               phba->sysfs_mbox.mbox = NULL;
+       }
+}
+
+static ssize_t
+sysfs_mbox_write(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+       struct Scsi_Host * host =
+               class_to_shost(container_of(kobj, struct class_device, kobj));
+       struct lpfc_hba * phba = (struct lpfc_hba*)host->hostdata[0];
+       struct lpfcMboxq * mbox = NULL;
+
+       if ((count + off) > MAILBOX_CMD_SIZE)
+               return -ERANGE;
+
+       if (off % 4 ||  count % 4 || (unsigned long)buf % 4)
+               return -EINVAL;
+
+       if (count == 0)
+               return 0;
+
+       if (off == 0) {
+               mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+               if (!mbox)
+                       return -ENOMEM;
+
+       }
+
+       spin_lock_irq(host->host_lock);
+
+       if (off == 0) {
+               if (phba->sysfs_mbox.mbox)
+                       mempool_free(mbox, phba->mbox_mem_pool);
+               else
+                       phba->sysfs_mbox.mbox = mbox;
+               phba->sysfs_mbox.state = SMBOX_WRITING;
+       } else {
+               if (phba->sysfs_mbox.state  != SMBOX_WRITING ||
+                   phba->sysfs_mbox.offset != off           ||
+                   phba->sysfs_mbox.mbox   == NULL ) {
+                       sysfs_mbox_idle(phba);
+                       spin_unlock_irq(host->host_lock);
+                       return -EINVAL;
+               }
+       }
+
+       memcpy((uint8_t *) & phba->sysfs_mbox.mbox->mb + off,
+              buf, count);
+
+       phba->sysfs_mbox.offset = off + count;
+
+       spin_unlock_irq(host->host_lock);
+
+       return count;
+}
+
+static ssize_t
+sysfs_mbox_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+       struct Scsi_Host *host =
+               class_to_shost(container_of(kobj, struct class_device,
+                                           kobj));
+       struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata[0];
+       int rc;
+
+       if (off > sizeof(MAILBOX_t))
+               return -ERANGE;
+
+       if ((count + off) > sizeof(MAILBOX_t))
+               count = sizeof(MAILBOX_t) - off;
+
+       if (off % 4 ||  count % 4 || (unsigned long)buf % 4)
+               return -EINVAL;
+
+       if (off && count == 0)
+               return 0;
+
+       spin_lock_irq(phba->host->host_lock);
+
+       if (off == 0 &&
+           phba->sysfs_mbox.state  == SMBOX_WRITING &&
+           phba->sysfs_mbox.offset >= 2 * sizeof(uint32_t)) {
+
+               switch (phba->sysfs_mbox.mbox->mb.mbxCommand) {
+                       /* Offline only */
+               case MBX_WRITE_NV:
+               case MBX_INIT_LINK:
+               case MBX_DOWN_LINK:
+               case MBX_CONFIG_LINK:
+               case MBX_CONFIG_RING:
+               case MBX_RESET_RING:
+               case MBX_UNREG_LOGIN:
+               case MBX_CLEAR_LA:
+               case MBX_DUMP_CONTEXT:
+               case MBX_RUN_DIAGS:
+               case MBX_RESTART:
+               case MBX_FLASH_WR_ULA:
+               case MBX_SET_MASK:
+               case MBX_SET_SLIM:
+               case MBX_SET_DEBUG:
+                       if (!(phba->fc_flag & FC_OFFLINE_MODE)) {
+                               printk(KERN_WARNING "mbox_read:Command 0x%x "
+                                      "is illegal in on-line state\n",
+                                      phba->sysfs_mbox.mbox->mb.mbxCommand);
+                               sysfs_mbox_idle(phba);
+                               spin_unlock_irq(phba->host->host_lock);
+                               return -EPERM;
+                       }
+               case MBX_LOAD_SM:
+               case MBX_READ_NV:
+               case MBX_READ_CONFIG:
+               case MBX_READ_RCONFIG:
+               case MBX_READ_STATUS:
+               case MBX_READ_XRI:
+               case MBX_READ_REV:
+               case MBX_READ_LNK_STAT:
+               case MBX_DUMP_MEMORY:
+               case MBX_DOWN_LOAD:
+               case MBX_UPDATE_CFG:
+               case MBX_LOAD_AREA:
+               case MBX_LOAD_EXP_ROM:
+                       break;
+               case MBX_READ_SPARM64:
+               case MBX_READ_LA:
+               case MBX_READ_LA64:
+               case MBX_REG_LOGIN:
+               case MBX_REG_LOGIN64:
+               case MBX_CONFIG_PORT:
+               case MBX_RUN_BIU_DIAG:
+                       printk(KERN_WARNING "mbox_read: Illegal Command 0x%x\n",
+                              phba->sysfs_mbox.mbox->mb.mbxCommand);
+                       sysfs_mbox_idle(phba);
+                       spin_unlock_irq(phba->host->host_lock);
+                       return -EPERM;
+               default:
+                       printk(KERN_WARNING "mbox_read: Unknown Command 0x%x\n",
+                              phba->sysfs_mbox.mbox->mb.mbxCommand);
+                       sysfs_mbox_idle(phba);
+                       spin_unlock_irq(phba->host->host_lock);
+                       return -EPERM;
+               }
+
+               if ((phba->fc_flag & FC_OFFLINE_MODE) ||
+                   (!(phba->sli.sli_flag & LPFC_SLI2_ACTIVE))){
+
+                       spin_unlock_irq(phba->host->host_lock);
+                       rc = lpfc_sli_issue_mbox (phba,
+                                                 phba->sysfs_mbox.mbox,
+                                                 MBX_POLL);
+                       spin_lock_irq(phba->host->host_lock);
+
+               } else {
+                       spin_unlock_irq(phba->host->host_lock);
+                       rc = lpfc_sli_issue_mbox_wait (phba,
+                                                      phba->sysfs_mbox.mbox,
+                                                      phba->fc_ratov * 2);
+                       spin_lock_irq(phba->host->host_lock);
+               }
+
+               if (rc != MBX_SUCCESS) {
+                       sysfs_mbox_idle(phba);
+                       spin_unlock_irq(host->host_lock);
+                       return -ENODEV;
+               }
+               phba->sysfs_mbox.state = SMBOX_READING;
+       }
+       else if (phba->sysfs_mbox.offset != off ||
+                phba->sysfs_mbox.state  != SMBOX_READING) {
+               printk(KERN_WARNING  "mbox_read: Bad State\n");
+               sysfs_mbox_idle(phba);
+               spin_unlock_irq(host->host_lock);
+               return -EINVAL;
+       }
+
+       memcpy(buf, (uint8_t *) & phba->sysfs_mbox.mbox->mb + off, count);
+
+       phba->sysfs_mbox.offset = off + count;
+
+       if (phba->sysfs_mbox.offset == sizeof(MAILBOX_t))
+               sysfs_mbox_idle(phba);
+
+       spin_unlock_irq(phba->host->host_lock);
+
+       return count;
+}
+
+static struct bin_attribute sysfs_mbox_attr = {
+       .attr = {
+               .name = "mbox",
+               .mode = S_IRUSR | S_IWUSR,
+               .owner = THIS_MODULE,
+       },
+       .size = sizeof(MAILBOX_t),
+       .read = sysfs_mbox_read,
+       .write = sysfs_mbox_write,
+};
+
+int
+lpfc_alloc_sysfs_attr(struct lpfc_hba *phba)
+{
+       struct Scsi_Host *host = phba->host;
+       int error;
+
+       error = sysfs_create_bin_file(&host->shost_classdev.kobj,
+                                                       &sysfs_ctlreg_attr);
+       if (error)
+               goto out;
+
+       error = sysfs_create_bin_file(&host->shost_classdev.kobj,
+                                                       &sysfs_mbox_attr);
+       if (error)
+               goto out_remove_ctlreg_attr;
+
+       return 0;
+out_remove_ctlreg_attr:
+       sysfs_remove_bin_file(&host->shost_classdev.kobj, &sysfs_ctlreg_attr);
+out:
+       return error;
+}
+
+void
+lpfc_free_sysfs_attr(struct lpfc_hba *phba)
+{
+       struct Scsi_Host *host = phba->host;
+
+       sysfs_remove_bin_file(&host->shost_classdev.kobj, &sysfs_mbox_attr);
+       sysfs_remove_bin_file(&host->shost_classdev.kobj, &sysfs_ctlreg_attr);
+}
+
+
+/*
+ * Dynamic FC Host Attributes Support
+ */
+
+static void
+lpfc_get_host_port_id(struct Scsi_Host *shost)
+{
+       struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0];
+       /* note: fc_myDID already in cpu endianness */
+       fc_host_port_id(shost) = phba->fc_myDID;
+}
+
+static void
+lpfc_get_host_port_type(struct Scsi_Host *shost)
+{
+       struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0];
+
+       spin_lock_irq(shost->host_lock);
+
+       if (phba->hba_state == LPFC_HBA_READY) {
+               if (phba->fc_topology == TOPOLOGY_LOOP) {
+                       if (phba->fc_flag & FC_PUBLIC_LOOP)
+                               fc_host_port_type(shost) = FC_PORTTYPE_NLPORT;
+                       else
+                               fc_host_port_type(shost) = FC_PORTTYPE_LPORT;
+               } else {
+                       if (phba->fc_flag & FC_FABRIC)
+                               fc_host_port_type(shost) = FC_PORTTYPE_NPORT;
+                       else
+                               fc_host_port_type(shost) = FC_PORTTYPE_PTP;
+               }
+       } else
+               fc_host_port_type(shost) = FC_PORTTYPE_UNKNOWN;
+
+       spin_unlock_irq(shost->host_lock);
+}
+
+static void
+lpfc_get_host_port_state(struct Scsi_Host *shost)
+{
+       struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0];
+
+       spin_lock_irq(shost->host_lock);
+
+       if (phba->fc_flag & FC_OFFLINE_MODE)
+               fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+       else {
+               switch (phba->hba_state) {
+               case LPFC_INIT_START:
+               case LPFC_INIT_MBX_CMDS:
+               case LPFC_LINK_DOWN:
+                       fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
+                       break;
+               case LPFC_LINK_UP:
+               case LPFC_LOCAL_CFG_LINK:
+               case LPFC_FLOGI:
+               case LPFC_FABRIC_CFG_LINK:
+               case LPFC_NS_REG:
+               case LPFC_NS_QRY:
+               case LPFC_BUILD_DISC_LIST:
+               case LPFC_DISC_AUTH:
+               case LPFC_CLEAR_LA:
+               case LPFC_HBA_READY:
+                       /* Links up, beyond this port_type reports state */
+                       fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+                       break;
+               case LPFC_HBA_ERROR:
+                       fc_host_port_state(shost) = FC_PORTSTATE_ERROR;
+                       break;
+               default:
+                       fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
+                       break;
+               }
+       }
+
+       spin_unlock_irq(shost->host_lock);
+}
+
+static void
+lpfc_get_host_speed(struct Scsi_Host *shost)
+{
+       struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0];
+
+       spin_lock_irq(shost->host_lock);
+
+       if (phba->hba_state == LPFC_HBA_READY) {
+               switch(phba->fc_linkspeed) {
+                       case LA_1GHZ_LINK:
+                               fc_host_speed(shost) = FC_PORTSPEED_1GBIT;
+                       break;
+                       case LA_2GHZ_LINK:
+                               fc_host_speed(shost) = FC_PORTSPEED_2GBIT;
+                       break;
+                       case LA_4GHZ_LINK:
+                               fc_host_speed(shost) = FC_PORTSPEED_4GBIT;
+                       break;
+                       default:
+                               fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN;
+                       break;
+               }
+       }
+
+       spin_unlock_irq(shost->host_lock);
+}
+
+static void
+lpfc_get_host_fabric_name (struct Scsi_Host *shost)
+{
+       struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0];
+       u64 nodename;
+
+       spin_lock_irq(shost->host_lock);
+
+       if ((phba->fc_flag & FC_FABRIC) ||
+           ((phba->fc_topology == TOPOLOGY_LOOP) &&
+            (phba->fc_flag & FC_PUBLIC_LOOP)))
+               memcpy(&nodename, &phba->fc_fabparam.nodeName, sizeof(u64));
+       else
+               /* fabric is local port if there is no F/FL_Port */
+               memcpy(&nodename, &phba->fc_nodename, sizeof(u64));
+
+       spin_unlock_irq(shost->host_lock);
+
+       fc_host_fabric_name(shost) = be64_to_cpu(nodename);
+}
+
+
+static struct fc_host_statistics *
+lpfc_get_stats(struct Scsi_Host *shost)
+{
+       struct lpfc_hba *phba = (struct lpfc_hba *)shost->hostdata[0];
+       struct lpfc_sli *psli = &phba->sli;
+       struct fc_host_statistics *hs =
+                       (struct fc_host_statistics *)phba->link_stats;
+       LPFC_MBOXQ_t *pmboxq;
+       MAILBOX_t *pmb;
+       int rc=0;
+
+       pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!pmboxq)
+               return NULL;
+       memset(pmboxq, 0, sizeof (LPFC_MBOXQ_t));
+
+       pmb = &pmboxq->mb;
+       pmb->mbxCommand = MBX_READ_STATUS;
+       pmb->mbxOwner = OWN_HOST;
+       pmboxq->context1 = NULL;
+
+       if ((phba->fc_flag & FC_OFFLINE_MODE) ||
+           (!(psli->sli_flag & LPFC_SLI2_ACTIVE))){
+               rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
+       } else
+               rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
+
+       if (rc != MBX_SUCCESS) {
+               if (pmboxq) {
+                       if (rc == MBX_TIMEOUT)
+                               pmboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+                       else
+                               mempool_free( pmboxq, phba->mbox_mem_pool);
+               }
+               return NULL;
+       }
+
+       hs->tx_frames = pmb->un.varRdStatus.xmitFrameCnt;
+       hs->tx_words = (pmb->un.varRdStatus.xmitByteCnt * 256);
+       hs->rx_frames = pmb->un.varRdStatus.rcvFrameCnt;
+       hs->rx_words = (pmb->un.varRdStatus.rcvByteCnt * 256);
+
+       memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t));
+       pmb->mbxCommand = MBX_READ_LNK_STAT;
+       pmb->mbxOwner = OWN_HOST;
+       pmboxq->context1 = NULL;
+
+       if ((phba->fc_flag & FC_OFFLINE_MODE) ||
+           (!(psli->sli_flag & LPFC_SLI2_ACTIVE))) {
+               rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL);
+       } else
+               rc = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2);
+
+       if (rc != MBX_SUCCESS) {
+               if (pmboxq) {
+                       if (rc == MBX_TIMEOUT)
+                               pmboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+                       else
+                               mempool_free( pmboxq, phba->mbox_mem_pool);
+               }
+               return NULL;
+       }
+
+       hs->link_failure_count = pmb->un.varRdLnk.linkFailureCnt;
+       hs->loss_of_sync_count = pmb->un.varRdLnk.lossSyncCnt;
+       hs->loss_of_signal_count = pmb->un.varRdLnk.lossSignalCnt;
+       hs->prim_seq_protocol_err_count = pmb->un.varRdLnk.primSeqErrCnt;
+       hs->invalid_tx_word_count = pmb->un.varRdLnk.invalidXmitWord;
+       hs->invalid_crc_count = pmb->un.varRdLnk.crcCnt;
+       hs->error_frames = pmb->un.varRdLnk.crcCnt;
+
+       if (phba->fc_topology == TOPOLOGY_LOOP) {
+               hs->lip_count = (phba->fc_eventTag >> 1);
+               hs->nos_count = -1;
+       } else {
+               hs->lip_count = -1;
+               hs->nos_count = (phba->fc_eventTag >> 1);
+       }
+
+       hs->dumped_frames = -1;
+
+/* FIX ME */
+       /*hs->SecondsSinceLastReset = (jiffies - lpfc_loadtime) / HZ;*/
+
+       return hs;
+}
+
+
+/*
+ * The LPFC driver treats linkdown handling as target loss events so there
+ * are no sysfs handlers for link_down_tmo.
+ */
+static void
+lpfc_get_starget_port_id(struct scsi_target *starget)
+{
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0];
+       uint32_t did = -1;
+       struct lpfc_nodelist *ndlp = NULL;
+
+       spin_lock_irq(shost->host_lock);
+       /* Search the mapped list for this target ID */
+       list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) {
+               if (starget->id == ndlp->nlp_sid) {
+                       did = ndlp->nlp_DID;
+                       break;
+               }
+       }
+       spin_unlock_irq(shost->host_lock);
+
+       fc_starget_port_id(starget) = did;
+}
+
+static void
+lpfc_get_starget_node_name(struct scsi_target *starget)
+{
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0];
+       uint64_t node_name = 0;
+       struct lpfc_nodelist *ndlp = NULL;
+
+       spin_lock_irq(shost->host_lock);
+       /* Search the mapped list for this target ID */
+       list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) {
+               if (starget->id == ndlp->nlp_sid) {
+                       memcpy(&node_name, &ndlp->nlp_nodename,
+                                               sizeof(struct lpfc_name));
+                       break;
+               }
+       }
+       spin_unlock_irq(shost->host_lock);
+
+       fc_starget_node_name(starget) = be64_to_cpu(node_name);
+}
+
+static void
+lpfc_get_starget_port_name(struct scsi_target *starget)
+{
+       struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0];
+       uint64_t port_name = 0;
+       struct lpfc_nodelist *ndlp = NULL;
+
+       spin_lock_irq(shost->host_lock);
+       /* Search the mapped list for this target ID */
+       list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) {
+               if (starget->id == ndlp->nlp_sid) {
+                       memcpy(&port_name, &ndlp->nlp_portname,
+                                               sizeof(struct lpfc_name));
+                       break;
+               }
+       }
+       spin_unlock_irq(shost->host_lock);
+
+       fc_starget_port_name(starget) = be64_to_cpu(port_name);
+}
+
+static void
+lpfc_get_rport_loss_tmo(struct fc_rport *rport)
+{
+       /*
+        * Return the driver's global value for device loss timeout plus
+        * five seconds to allow the driver's nodev timer to run.
+        */
+       rport->dev_loss_tmo = lpfc_nodev_tmo + 5;
+}
+
+static void
+lpfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
+{
+       /*
+        * The driver doesn't have a per-target timeout setting.  Set
+        * this value globally. lpfc_nodev_tmo should be greater then 0.
+        */
+       if (timeout)
+               lpfc_nodev_tmo = timeout;
+       else
+               lpfc_nodev_tmo = 1;
+       rport->dev_loss_tmo = lpfc_nodev_tmo + 5;
+}
+
+
+#define lpfc_rport_show_function(field, format_string, sz, cast)       \
+static ssize_t                                                         \
+lpfc_show_rport_##field (struct class_device *cdev, char *buf)         \
+{                                                                      \
+       struct fc_rport *rport = transport_class_to_rport(cdev);        \
+       struct lpfc_rport_data *rdata = rport->hostdata;                \
+       return snprintf(buf, sz, format_string,                         \
+               (rdata->target) ? cast rdata->target->field : 0);       \
+}
+
+#define lpfc_rport_rd_attr(field, format_string, sz)                   \
+       lpfc_rport_show_function(field, format_string, sz, )            \
+static FC_RPORT_ATTR(field, S_IRUGO, lpfc_show_rport_##field, NULL)
+
+
+struct fc_function_template lpfc_transport_functions = {
+       /* fixed attributes the driver supports */
+       .show_host_node_name = 1,
+       .show_host_port_name = 1,
+       .show_host_supported_classes = 1,
+       .show_host_supported_fc4s = 1,
+       .show_host_symbolic_name = 1,
+       .show_host_supported_speeds = 1,
+       .show_host_maxframe_size = 1,
+
+       /* dynamic attributes the driver supports */
+       .get_host_port_id = lpfc_get_host_port_id,
+       .show_host_port_id = 1,
+
+       .get_host_port_type = lpfc_get_host_port_type,
+       .show_host_port_type = 1,
+
+       .get_host_port_state = lpfc_get_host_port_state,
+       .show_host_port_state = 1,
+
+       /* active_fc4s is shown but doesn't change (thus no get function) */
+       .show_host_active_fc4s = 1,
+
+       .get_host_speed = lpfc_get_host_speed,
+       .show_host_speed = 1,
+
+       .get_host_fabric_name = lpfc_get_host_fabric_name,
+       .show_host_fabric_name = 1,
+
+       /*
+        * The LPFC driver treats linkdown handling as target loss events
+        * so there are no sysfs handlers for link_down_tmo.
+        */
+
+       .get_fc_host_stats = lpfc_get_stats,
+
+       /* the LPFC driver doesn't support resetting stats yet */
+
+       .dd_fcrport_size = sizeof(struct lpfc_rport_data),
+       .show_rport_maxframe_size = 1,
+       .show_rport_supported_classes = 1,
+
+       .get_rport_dev_loss_tmo = lpfc_get_rport_loss_tmo,
+       .set_rport_dev_loss_tmo = lpfc_set_rport_loss_tmo,
+       .show_rport_dev_loss_tmo = 1,
+
+       .get_starget_port_id  = lpfc_get_starget_port_id,
+       .show_starget_port_id = 1,
+
+       .get_starget_node_name = lpfc_get_starget_node_name,
+       .show_starget_node_name = 1,
+
+       .get_starget_port_name = lpfc_get_starget_port_name,
+       .show_starget_port_name = 1,
+};
+
+void
+lpfc_get_cfgparam(struct lpfc_hba *phba)
+{
+       phba->cfg_log_verbose = lpfc_log_verbose;
+       phba->cfg_cr_delay = lpfc_cr_delay;
+       phba->cfg_cr_count = lpfc_cr_count;
+       phba->cfg_lun_queue_depth = lpfc_lun_queue_depth;
+       phba->cfg_fcp_class = lpfc_fcp_class;
+       phba->cfg_use_adisc = lpfc_use_adisc;
+       phba->cfg_ack0 = lpfc_ack0;
+       phba->cfg_topology = lpfc_topology;
+       phba->cfg_scan_down = lpfc_scan_down;
+       phba->cfg_nodev_tmo = lpfc_nodev_tmo;
+       phba->cfg_link_speed = lpfc_link_speed;
+       phba->cfg_fdmi_on = lpfc_fdmi_on;
+       phba->cfg_discovery_threads = lpfc_discovery_threads;
+       phba->cfg_max_luns = lpfc_max_luns;
+
+       /*
+        * The total number of segments is the configuration value plus 2
+        * since the IOCB need a command and response bde.
+        */
+       phba->cfg_sg_seg_cnt = LPFC_SG_SEG_CNT + 2;
+
+       /*
+        * Since the sg_tablesize is module parameter, the sg_dma_buf_size
+        * used to create the sg_dma_buf_pool must be dynamically calculated
+        */
+       phba->cfg_sg_dma_buf_size = sizeof(struct fcp_cmnd) +
+                       sizeof(struct fcp_rsp) +
+                       (phba->cfg_sg_seg_cnt * sizeof(struct ulp_bde64));
+
+       switch (phba->pcidev->device) {
+       case PCI_DEVICE_ID_LP101:
+       case PCI_DEVICE_ID_BSMB:
+       case PCI_DEVICE_ID_ZSMB:
+               phba->cfg_hba_queue_depth = LPFC_LP101_HBA_Q_DEPTH;
+               break;
+       case PCI_DEVICE_ID_RFLY:
+       case PCI_DEVICE_ID_PFLY:
+       case PCI_DEVICE_ID_BMID:
+       case PCI_DEVICE_ID_ZMID:
+       case PCI_DEVICE_ID_TFLY:
+               phba->cfg_hba_queue_depth = LPFC_LC_HBA_Q_DEPTH;
+               break;
+       default:
+               phba->cfg_hba_queue_depth = LPFC_DFT_HBA_Q_DEPTH;
+       }
+       return;
+}
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
new file mode 100644 (file)
index 0000000..42fab03
--- /dev/null
@@ -0,0 +1,1246 @@
+/*******************************************************************
+ * 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.c 1.37 2005/04/13 14:27:09EDT sf_support Exp  $
+ */
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "lpfc_version.h"
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_crtn.h"
+
+#define LPFC_RESET_WAIT  2
+#define LPFC_ABORT_WAIT  2
+
+static inline void lpfc_put_lun(struct fcp_cmnd *fcmd, unsigned int lun)
+{
+       fcmd->fcpLunLsl = 0;
+       fcmd->fcpLunMsl = swab16((uint16_t)lun);
+}
+
+/*
+ * This routine allocates a scsi buffer, which contains all the necessary
+ * information needed to initiate a SCSI I/O.  The non-DMAable buffer region
+ * contains information to build the IOCB.  The DMAable region contains
+ * memory for the FCP CMND, FCP RSP, and the inital BPL.  In addition to
+ * allocating memeory, the FCP CMND and FCP RSP BDEs are setup in the BPL
+ * and the BPL BDE is setup in the IOCB.
+ */
+static struct lpfc_scsi_buf *
+lpfc_get_scsi_buf(struct lpfc_hba * phba)
+{
+       struct lpfc_scsi_buf *psb;
+       struct ulp_bde64 *bpl;
+       IOCB_t *iocb;
+       dma_addr_t pdma_phys;
+
+       psb = kmalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL);
+       if (!psb)
+               return NULL;
+       memset(psb, 0, sizeof (struct lpfc_scsi_buf));
+       psb->scsi_hba = phba;
+
+       /*
+        * Get memory from the pci pool to map the virt space to pci bus space
+        * for an I/O.  The DMA buffer includes space for the struct fcp_cmnd,
+        * struct fcp_rsp and the number of bde's necessary to support the
+        * sg_tablesize.
+        */
+       psb->data = pci_pool_alloc(phba->lpfc_scsi_dma_buf_pool, GFP_KERNEL,
+                                                       &psb->dma_handle);
+       if (!psb->data) {
+               kfree(psb);
+               return NULL;
+       }
+
+       /* Initialize virtual ptrs to dma_buf region. */
+       memset(psb->data, 0, phba->cfg_sg_dma_buf_size);
+
+       psb->fcp_cmnd = psb->data;
+       psb->fcp_rsp = psb->data + sizeof(struct fcp_cmnd);
+       psb->fcp_bpl = psb->data + sizeof(struct fcp_cmnd) +
+                                                       sizeof(struct fcp_rsp);
+
+       /* Initialize local short-hand pointers. */
+       bpl = psb->fcp_bpl;
+       pdma_phys = psb->dma_handle;
+
+       /*
+        * The first two bdes are the FCP_CMD and FCP_RSP.  The balance are sg
+        * list bdes.  Initialize the first two and leave the rest for
+        * queuecommand.
+        */
+       bpl->addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys));
+       bpl->addrLow = le32_to_cpu(putPaddrLow(pdma_phys));
+       bpl->tus.f.bdeSize = sizeof (struct fcp_cmnd);
+       bpl->tus.f.bdeFlags = BUFF_USE_CMND;
+       bpl->tus.w = le32_to_cpu(bpl->tus.w);
+       bpl++;
+
+       /* Setup the physical region for the FCP RSP */
+       pdma_phys += sizeof (struct fcp_cmnd);
+       bpl->addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys));
+       bpl->addrLow = le32_to_cpu(putPaddrLow(pdma_phys));
+       bpl->tus.f.bdeSize = sizeof (struct fcp_rsp);
+       bpl->tus.f.bdeFlags = (BUFF_USE_CMND | BUFF_USE_RCV);
+       bpl->tus.w = le32_to_cpu(bpl->tus.w);
+
+       /*
+        * Since the IOCB for the FCP I/O is built into this lpfc_scsi_buf,
+        * initialize it with all known data now.
+        */
+       pdma_phys += (sizeof (struct fcp_rsp));
+       iocb = &psb->cur_iocbq.iocb;
+       iocb->un.fcpi64.bdl.ulpIoTag32 = 0;
+       iocb->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys);
+       iocb->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys);
+       iocb->un.fcpi64.bdl.bdeSize = (2 * sizeof (struct ulp_bde64));
+       iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDL;
+       iocb->ulpBdeCount = 1;
+       iocb->ulpClass = CLASS3;
+
+       return psb;
+}
+
+static void
+lpfc_free_scsi_buf(struct lpfc_scsi_buf * psb)
+{
+       struct lpfc_hba *phba = psb->scsi_hba;
+
+       /*
+        * There are only two special cases to consider.  (1) the scsi command
+        * requested scatter-gather usage or (2) the scsi command allocated
+        * a request buffer, but did not request use_sg.  There is a third
+        * case, but it does not require resource deallocation.
+        */
+       if ((psb->seg_cnt > 0) && (psb->pCmd->use_sg)) {
+               dma_unmap_sg(&phba->pcidev->dev, psb->pCmd->request_buffer,
+                               psb->seg_cnt, psb->pCmd->sc_data_direction);
+       } else {
+                if ((psb->nonsg_phys) && (psb->pCmd->request_bufflen)) {
+                       dma_unmap_single(&phba->pcidev->dev, psb->nonsg_phys,
+                                               psb->pCmd->request_bufflen,
+                                               psb->pCmd->sc_data_direction);
+                }
+       }
+
+       list_add_tail(&psb->list, &phba->lpfc_scsi_buf_list);
+}
+
+static int
+lpfc_scsi_prep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * lpfc_cmd)
+{
+       struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
+       struct scatterlist *sgel = NULL;
+       struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+       struct ulp_bde64 *bpl = lpfc_cmd->fcp_bpl;
+       IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
+       dma_addr_t physaddr;
+       uint32_t i, num_bde = 0;
+       int datadir = scsi_cmnd->sc_data_direction;
+       int dma_error;
+
+       /*
+        * There are three possibilities here - use scatter-gather segment, use
+        * the single mapping, or neither.  Start the lpfc command prep by
+        * bumping the bpl beyond the fcp_cmnd and fcp_rsp regions to the first
+        * data bde entry.
+        */
+       bpl += 2;
+       if (scsi_cmnd->use_sg) {
+               /*
+                * The driver stores the segment count returned from pci_map_sg
+                * because this a count of dma-mappings used to map the use_sg
+                * pages.  They are not guaranteed to be the same for those
+                * architectures that implement an IOMMU.
+                */
+               sgel = (struct scatterlist *)scsi_cmnd->request_buffer;
+               lpfc_cmd->seg_cnt = dma_map_sg(&phba->pcidev->dev, sgel,
+                                               scsi_cmnd->use_sg, datadir);
+               if (lpfc_cmd->seg_cnt == 0)
+                       return 1;
+
+               if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
+                       printk(KERN_ERR "%s: Too many sg segments from "
+                              "dma_map_sg.  Config %d, seg_cnt %d",
+                              __FUNCTION__, phba->cfg_sg_seg_cnt,
+                              lpfc_cmd->seg_cnt);
+                       dma_unmap_sg(&phba->pcidev->dev, sgel,
+                                    lpfc_cmd->seg_cnt, datadir);
+                       return 1;
+               }
+
+               /*
+                * The driver established a maximum scatter-gather segment count
+                * during probe that limits the number of sg elements in any
+                * single scsi command.  Just run through the seg_cnt and format
+                * the bde's.
+                */
+               for (i = 0; i < lpfc_cmd->seg_cnt; i++) {
+                       physaddr = sg_dma_address(sgel);
+                       bpl->addrLow = le32_to_cpu(putPaddrLow(physaddr));
+                       bpl->addrHigh = le32_to_cpu(putPaddrHigh(physaddr));
+                       bpl->tus.f.bdeSize = sg_dma_len(sgel);
+                       if (datadir == DMA_TO_DEVICE)
+                               bpl->tus.f.bdeFlags = 0;
+                       else
+                               bpl->tus.f.bdeFlags = BUFF_USE_RCV;
+                       bpl->tus.w = le32_to_cpu(bpl->tus.w);
+                       bpl++;
+                       sgel++;
+                       num_bde++;
+               }
+       } else if (scsi_cmnd->request_buffer && scsi_cmnd->request_bufflen) {
+               physaddr = dma_map_single(&phba->pcidev->dev,
+                                         scsi_cmnd->request_buffer,
+                                         scsi_cmnd->request_bufflen,
+                                         datadir);
+               dma_error = dma_mapping_error(physaddr);
+               if (dma_error) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+                               "%d:0718 Unable to dma_map_single "
+                               "request_buffer: x%x\n",
+                               phba->brd_no, dma_error);
+                       return 1;
+               }
+
+               lpfc_cmd->nonsg_phys = physaddr;
+               bpl->addrLow = le32_to_cpu(putPaddrLow(physaddr));
+               bpl->addrHigh = le32_to_cpu(putPaddrHigh(physaddr));
+               bpl->tus.f.bdeSize = scsi_cmnd->request_bufflen;
+               if (datadir == DMA_TO_DEVICE)
+                       bpl->tus.f.bdeFlags = 0;
+               bpl->tus.w = le32_to_cpu(bpl->tus.w);
+               num_bde = 1;
+               bpl++;
+       }
+
+       /*
+        * Finish initializing those IOCB fields that are dependent on the
+        * scsi_cmnd request_buffer
+        */
+       iocb_cmd->un.fcpi64.bdl.bdeSize +=
+               (num_bde * sizeof (struct ulp_bde64));
+       iocb_cmd->ulpBdeCount = 1;
+       iocb_cmd->ulpLe = 1;
+       fcp_cmnd->fcpDl = be32_to_cpu(scsi_cmnd->request_bufflen);
+       return 0;
+}
+
+static void
+lpfc_handle_fcp_err(struct lpfc_scsi_buf *lpfc_cmd)
+{
+       struct scsi_cmnd *cmnd = lpfc_cmd->pCmd;
+       struct fcp_cmnd *fcpcmd = lpfc_cmd->fcp_cmnd;
+       struct fcp_rsp *fcprsp = lpfc_cmd->fcp_rsp;
+       struct lpfc_hba *phba = lpfc_cmd->scsi_hba;
+       uint32_t fcpi_parm = lpfc_cmd->cur_iocbq.iocb.un.fcpi.fcpi_parm;
+       uint32_t resp_info = fcprsp->rspStatus2;
+       uint32_t scsi_status = fcprsp->rspStatus3;
+       uint32_t host_status = DID_OK;
+       uint32_t rsplen = 0;
+
+       /*
+        *  If this is a task management command, there is no
+        *  scsi packet associated with this lpfc_cmd.  The driver
+        *  consumes it.
+        */
+       if (fcpcmd->fcpCntl2) {
+               scsi_status = 0;
+               goto out;
+       }
+
+       lpfc_printf_log(phba, KERN_WARNING, LOG_FCP,
+                       "%d:0730 FCP command failed: RSP "
+                       "Data: x%x x%x x%x x%x x%x x%x\n",
+                       phba->brd_no, resp_info, scsi_status,
+                       be32_to_cpu(fcprsp->rspResId),
+                       be32_to_cpu(fcprsp->rspSnsLen),
+                       be32_to_cpu(fcprsp->rspRspLen),
+                       fcprsp->rspInfo3);
+
+       if (resp_info & RSP_LEN_VALID) {
+               rsplen = be32_to_cpu(fcprsp->rspRspLen);
+               if ((rsplen != 0 && rsplen != 4 && rsplen != 8) ||
+                   (fcprsp->rspInfo3 != RSP_NO_FAILURE)) {
+                       host_status = DID_ERROR;
+                       goto out;
+               }
+       }
+
+       if ((resp_info & SNS_LEN_VALID) && fcprsp->rspSnsLen) {
+               uint32_t snslen = be32_to_cpu(fcprsp->rspSnsLen);
+               if (snslen > SCSI_SENSE_BUFFERSIZE)
+                       snslen = SCSI_SENSE_BUFFERSIZE;
+
+               memcpy(cmnd->sense_buffer, &fcprsp->rspInfo0 + rsplen, snslen);
+       }
+
+       cmnd->resid = 0;
+       if (resp_info & RESID_UNDER) {
+               cmnd->resid = be32_to_cpu(fcprsp->rspResId);
+
+               lpfc_printf_log(phba, KERN_INFO, LOG_FCP,
+                               "%d:0716 FCP Read Underrun, expected %d, "
+                               "residual %d Data: x%x x%x x%x\n", phba->brd_no,
+                               be32_to_cpu(fcpcmd->fcpDl), cmnd->resid,
+                               fcpi_parm, cmnd->cmnd[0], cmnd->underflow);
+
+               /*
+                * The cmnd->underflow is the minimum number of bytes that must
+                * be transfered for this command.  Provided a sense condition
+                * is not present, make sure the actual amount transferred is at
+                * least the underflow value or fail.
+                */
+               if (!(resp_info & SNS_LEN_VALID) &&
+                   (scsi_status == SAM_STAT_GOOD) &&
+                   (cmnd->request_bufflen - cmnd->resid) < cmnd->underflow) {
+                       lpfc_printf_log(phba, KERN_INFO, LOG_FCP,
+                                       "%d:0717 FCP command x%x residual "
+                                       "underrun converted to error "
+                                       "Data: x%x x%x x%x\n", phba->brd_no,
+                                       cmnd->cmnd[0], cmnd->request_bufflen,
+                                       cmnd->resid, cmnd->underflow);
+
+                       host_status = DID_ERROR;
+               }
+       } else if (resp_info & RESID_OVER) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_FCP,
+                               "%d:0720 FCP command x%x residual "
+                               "overrun error. Data: x%x x%x \n",
+                               phba->brd_no, cmnd->cmnd[0],
+                               cmnd->request_bufflen, cmnd->resid);
+               host_status = DID_ERROR;
+
+       /*
+        * Check SLI validation that all the transfer was actually done
+        * (fcpi_parm should be zero). Apply check only to reads.
+        */
+       } else if ((scsi_status == SAM_STAT_GOOD) && fcpi_parm &&
+                       (cmnd->sc_data_direction == DMA_FROM_DEVICE)) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_FCP,
+                       "%d:0734 FCP Read Check Error Data: "
+                       "x%x x%x x%x x%x\n", phba->brd_no,
+                       be32_to_cpu(fcpcmd->fcpDl),
+                       be32_to_cpu(fcprsp->rspResId),
+                       fcpi_parm, cmnd->cmnd[0]);
+               host_status = DID_ERROR;
+               cmnd->resid = cmnd->request_bufflen;
+       }
+
+ out:
+       cmnd->result = ScsiResult(host_status, scsi_status);
+}
+
+static void
+lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
+                       struct lpfc_iocbq *pIocbOut)
+{
+       struct lpfc_scsi_buf *lpfc_cmd =
+               (struct lpfc_scsi_buf *) pIocbIn->context1;
+       struct lpfc_rport_data *rdata = lpfc_cmd->rdata;
+       struct lpfc_nodelist *pnode = rdata->pnode;
+       struct scsi_cmnd *cmd = lpfc_cmd->pCmd;
+       unsigned long iflag;
+
+       lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4];
+       lpfc_cmd->status = pIocbOut->iocb.ulpStatus;
+
+       if (lpfc_cmd->status) {
+               if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT &&
+                   (lpfc_cmd->result & IOERR_DRVR_MASK))
+                       lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
+               else if (lpfc_cmd->status >= IOSTAT_CNT)
+                       lpfc_cmd->status = IOSTAT_DEFAULT;
+
+               lpfc_printf_log(phba, KERN_WARNING, LOG_FCP,
+                               "%d:0729 FCP cmd x%x failed <%d/%d> status: "
+                               "x%x result: x%x Data: x%x x%x\n",
+                               phba->brd_no, cmd->cmnd[0], cmd->device->id,
+                               cmd->device->lun, lpfc_cmd->status,
+                               lpfc_cmd->result, pIocbOut->iocb.ulpContext,
+                               lpfc_cmd->cur_iocbq.iocb.ulpIoTag);
+
+               switch (lpfc_cmd->status) {
+               case IOSTAT_FCP_RSP_ERROR:
+                       /* Call FCP RSP handler to determine result */
+                       lpfc_handle_fcp_err(lpfc_cmd);
+                       break;
+               case IOSTAT_NPORT_BSY:
+               case IOSTAT_FABRIC_BSY:
+                       cmd->result = ScsiResult(DID_BUS_BUSY, 0);
+                       break;
+               default:
+                       cmd->result = ScsiResult(DID_ERROR, 0);
+                       break;
+               }
+
+               if (pnode) {
+                       if (pnode->nlp_state != NLP_STE_MAPPED_NODE)
+                               cmd->result = ScsiResult(DID_BUS_BUSY,
+                                       SAM_STAT_BUSY);
+               }
+               else {
+                       cmd->result = ScsiResult(DID_NO_CONNECT, 0);
+               }
+       } else {
+               cmd->result = ScsiResult(DID_OK, 0);
+       }
+
+       if (cmd->result || lpfc_cmd->fcp_rsp->rspSnsLen) {
+               uint32_t *lp = (uint32_t *)cmd->sense_buffer;
+
+               lpfc_printf_log(phba, KERN_INFO, LOG_FCP,
+                               "%d:0710 Iodone <%d/%d> cmd %p, error x%x "
+                               "SNS x%x x%x Data: x%x x%x\n",
+                               phba->brd_no, cmd->device->id,
+                               cmd->device->lun, cmd, cmd->result,
+                               *lp, *(lp + 3), cmd->retries, cmd->resid);
+       }
+
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+       lpfc_free_scsi_buf(lpfc_cmd);
+       cmd->host_scribble = NULL;
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+
+       cmd->scsi_done(cmd);
+}
+
+static void
+lpfc_scsi_prep_cmnd(struct lpfc_hba * phba, struct lpfc_scsi_buf * lpfc_cmd,
+                       struct lpfc_nodelist *pnode)
+{
+       struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
+       struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+       IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
+       struct lpfc_iocbq *piocbq = &(lpfc_cmd->cur_iocbq);
+       int datadir = scsi_cmnd->sc_data_direction;
+
+       lpfc_cmd->fcp_rsp->rspSnsLen = 0;
+
+       lpfc_put_lun(lpfc_cmd->fcp_cmnd, lpfc_cmd->pCmd->device->lun);
+
+       memcpy(&fcp_cmnd->fcpCdb[0], scsi_cmnd->cmnd, 16);
+
+       if (scsi_cmnd->device->tagged_supported) {
+               switch (scsi_cmnd->tag) {
+               case HEAD_OF_QUEUE_TAG:
+                       fcp_cmnd->fcpCntl1 = HEAD_OF_Q;
+                       break;
+               case ORDERED_QUEUE_TAG:
+                       fcp_cmnd->fcpCntl1 = ORDERED_Q;
+                       break;
+               default:
+                       fcp_cmnd->fcpCntl1 = SIMPLE_Q;
+                       break;
+               }
+       } else
+               fcp_cmnd->fcpCntl1 = 0;
+
+       /*
+        * There are three possibilities here - use scatter-gather segment, use
+        * the single mapping, or neither.  Start the lpfc command prep by
+        * bumping the bpl beyond the fcp_cmnd and fcp_rsp regions to the first
+        * data bde entry.
+        */
+       if (scsi_cmnd->use_sg) {
+               if (datadir == DMA_TO_DEVICE) {
+                       iocb_cmd->ulpCommand = CMD_FCP_IWRITE64_CR;
+                       iocb_cmd->un.fcpi.fcpi_parm = 0;
+                       iocb_cmd->ulpPU = 0;
+                       fcp_cmnd->fcpCntl3 = WRITE_DATA;
+                       phba->fc4OutputRequests++;
+               } else {
+                       iocb_cmd->ulpCommand = CMD_FCP_IREAD64_CR;
+                       iocb_cmd->ulpPU = PARM_READ_CHECK;
+                       iocb_cmd->un.fcpi.fcpi_parm =
+                               scsi_cmnd->request_bufflen;
+                       fcp_cmnd->fcpCntl3 = READ_DATA;
+                       phba->fc4InputRequests++;
+               }
+       } else if (scsi_cmnd->request_buffer && scsi_cmnd->request_bufflen) {
+               if (datadir == DMA_TO_DEVICE) {
+                       iocb_cmd->ulpCommand = CMD_FCP_IWRITE64_CR;
+                       iocb_cmd->un.fcpi.fcpi_parm = 0;
+                       iocb_cmd->ulpPU = 0;
+                       fcp_cmnd->fcpCntl3 = WRITE_DATA;
+                       phba->fc4OutputRequests++;
+               } else {
+                       iocb_cmd->ulpCommand = CMD_FCP_IREAD64_CR;
+                       iocb_cmd->ulpPU = PARM_READ_CHECK;
+                       iocb_cmd->un.fcpi.fcpi_parm =
+                               scsi_cmnd->request_bufflen;
+                       fcp_cmnd->fcpCntl3 = READ_DATA;
+                       phba->fc4InputRequests++;
+               }
+       } else {
+               iocb_cmd->ulpCommand = CMD_FCP_ICMND64_CR;
+               iocb_cmd->un.fcpi.fcpi_parm = 0;
+               iocb_cmd->ulpPU = 0;
+               fcp_cmnd->fcpCntl3 = 0;
+               phba->fc4ControlRequests++;
+       }
+
+       /*
+        * Finish initializing those IOCB fields that are independent
+        * of the scsi_cmnd request_buffer
+        */
+       piocbq->iocb.ulpContext = pnode->nlp_rpi;
+       if (pnode->nlp_fcp_info & NLP_FCP_2_DEVICE)
+               piocbq->iocb.ulpFCP2Rcvy = 1;
+
+       piocbq->iocb.ulpClass = (pnode->nlp_fcp_info & 0x0f);
+       piocbq->context1  = lpfc_cmd;
+       piocbq->iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl;
+       piocbq->iocb.ulpTimeout = lpfc_cmd->timeout;
+}
+
+static int
+lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_hba *phba,
+                            struct lpfc_scsi_buf *lpfc_cmd,
+                            uint8_t task_mgmt_cmd)
+{
+       struct lpfc_sli *psli;
+       struct lpfc_iocbq *piocbq;
+       IOCB_t *piocb;
+       struct fcp_cmnd *fcp_cmnd;
+       struct scsi_device *scsi_dev = lpfc_cmd->pCmd->device;
+       struct lpfc_rport_data *rdata = scsi_dev->hostdata;
+       struct lpfc_nodelist *ndlp = rdata->pnode;
+
+       if ((ndlp == 0) || (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) {
+               return 0;
+       }
+
+       psli = &phba->sli;
+       piocbq = &(lpfc_cmd->cur_iocbq);
+       piocb = &piocbq->iocb;
+
+       fcp_cmnd = lpfc_cmd->fcp_cmnd;
+       lpfc_put_lun(lpfc_cmd->fcp_cmnd, lpfc_cmd->pCmd->device->lun);
+       fcp_cmnd->fcpCntl2 = task_mgmt_cmd;
+
+       piocb->ulpCommand = CMD_FCP_ICMND64_CR;
+
+       piocb->ulpContext = ndlp->nlp_rpi;
+       if (ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE) {
+               piocb->ulpFCP2Rcvy = 1;
+       }
+       piocb->ulpClass = (ndlp->nlp_fcp_info & 0x0f);
+
+       /* ulpTimeout is only one byte */
+       if (lpfc_cmd->timeout > 0xff) {
+               /*
+                * Do not timeout the command at the firmware level.
+                * The driver will provide the timeout mechanism.
+                */
+               piocb->ulpTimeout = 0;
+       } else {
+               piocb->ulpTimeout = lpfc_cmd->timeout;
+       }
+
+       lpfc_cmd->rdata = rdata;
+
+       switch (task_mgmt_cmd) {
+       case FCP_LUN_RESET:
+               /* Issue LUN Reset to TGT <num> LUN <num> */
+               lpfc_printf_log(phba,
+                               KERN_INFO,
+                               LOG_FCP,
+                               "%d:0703 Issue LUN Reset to TGT %d LUN %d "
+                               "Data: x%x x%x\n",
+                               phba->brd_no,
+                               scsi_dev->id, scsi_dev->lun,
+                               ndlp->nlp_rpi, ndlp->nlp_flag);
+
+               break;
+       case FCP_ABORT_TASK_SET:
+               /* Issue Abort Task Set to TGT <num> LUN <num> */
+               lpfc_printf_log(phba,
+                               KERN_INFO,
+                               LOG_FCP,
+                               "%d:0701 Issue Abort Task Set to TGT %d LUN %d "
+                               "Data: x%x x%x\n",
+                               phba->brd_no,
+                               scsi_dev->id, scsi_dev->lun,
+                               ndlp->nlp_rpi, ndlp->nlp_flag);
+
+               break;
+       case FCP_TARGET_RESET:
+               /* Issue Target Reset to TGT <num> */
+               lpfc_printf_log(phba,
+                               KERN_INFO,
+                               LOG_FCP,
+                               "%d:0702 Issue Target Reset to TGT %d "
+                               "Data: x%x x%x\n",
+                               phba->brd_no,
+                               scsi_dev->id, ndlp->nlp_rpi,
+                               ndlp->nlp_flag);
+               break;
+       }
+
+       return (1);
+}
+
+static int
+lpfc_scsi_tgt_reset(struct lpfc_scsi_buf * lpfc_cmd, struct lpfc_hba * phba)
+{
+       struct lpfc_iocbq *iocbq;
+       struct lpfc_iocbq *iocbqrsp = NULL;
+       struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list;
+       int ret;
+
+       ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, FCP_TARGET_RESET);
+       if (!ret)
+               return FAILED;
+
+       lpfc_cmd->scsi_hba = phba;
+       iocbq = &lpfc_cmd->cur_iocbq;
+       list_remove_head(lpfc_iocb_list, iocbqrsp, struct lpfc_iocbq, list);
+       if (!iocbqrsp)
+               return FAILED;
+       memset(iocbqrsp, 0, sizeof (struct lpfc_iocbq));
+
+       iocbq->iocb_flag |= LPFC_IO_POLL;
+       ret = lpfc_sli_issue_iocb_wait_high_priority(phba,
+                    &phba->sli.ring[phba->sli.fcp_ring],
+                    iocbq, SLI_IOCB_HIGH_PRIORITY,
+                    iocbqrsp,
+                    lpfc_cmd->timeout);
+       if (ret != IOCB_SUCCESS) {
+               lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
+               ret = FAILED;
+       } else {
+               ret = SUCCESS;
+               lpfc_cmd->result = iocbqrsp->iocb.un.ulpWord[4];
+               lpfc_cmd->status = iocbqrsp->iocb.ulpStatus;
+               if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT &&
+                       (lpfc_cmd->result & IOERR_DRVR_MASK))
+                               lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
+       }
+
+       /*
+        * All outstanding txcmplq I/Os should have been aborted by the target.
+        * Unfortunately, some targets do not abide by this forcing the driver
+        * to double check.
+        */
+       lpfc_sli_abort_iocb(phba, &phba->sli.ring[phba->sli.fcp_ring],
+                           lpfc_cmd->pCmd->device->id,
+                           lpfc_cmd->pCmd->device->lun, 0, LPFC_CTX_TGT);
+
+       /* Return response IOCB to free list. */
+       list_add_tail(&iocbqrsp->list, lpfc_iocb_list);
+       return ret;
+}
+
+static void
+lpfc_scsi_cmd_iocb_cleanup (struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
+                           struct lpfc_iocbq *pIocbOut)
+{
+       unsigned long iflag;
+       struct lpfc_scsi_buf *lpfc_cmd =
+               (struct lpfc_scsi_buf *) pIocbIn->context1;
+
+       spin_lock_irqsave(phba->host->host_lock, iflag);
+       lpfc_free_scsi_buf(lpfc_cmd);
+       spin_unlock_irqrestore(phba->host->host_lock, iflag);
+}
+
+static void
+lpfc_scsi_cmd_iocb_cmpl_aborted(struct lpfc_hba *phba,
+                               struct lpfc_iocbq *pIocbIn,
+                               struct lpfc_iocbq *pIocbOut)
+{
+       struct scsi_cmnd *ml_cmd =
+               ((struct lpfc_scsi_buf *) pIocbIn->context1)->pCmd;
+
+       lpfc_scsi_cmd_iocb_cleanup (phba, pIocbIn, pIocbOut);
+       ml_cmd->host_scribble = NULL;
+}
+
+const char *
+lpfc_info(struct Scsi_Host *host)
+{
+       struct lpfc_hba    *phba = (struct lpfc_hba *) host->hostdata[0];
+       int len;
+       static char  lpfcinfobuf[384];
+
+       memset(lpfcinfobuf,0,384);
+       if (phba && phba->pcidev){
+               strncpy(lpfcinfobuf, phba->ModelDesc, 256);
+               len = strlen(lpfcinfobuf);
+               snprintf(lpfcinfobuf + len,
+                       384-len,
+                       " on PCI bus %02x device %02x irq %d",
+                       phba->pcidev->bus->number,
+                       phba->pcidev->devfn,
+                       phba->pcidev->irq);
+               len = strlen(lpfcinfobuf);
+               if (phba->Port[0]) {
+                       snprintf(lpfcinfobuf + len,
+                                384-len,
+                                " port %s",
+                                phba->Port);
+               }
+       }
+       return lpfcinfobuf;
+}
+
+static int
+lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
+{
+       struct lpfc_hba *phba =
+               (struct lpfc_hba *) cmnd->device->host->hostdata[0];
+       struct lpfc_sli *psli = &phba->sli;
+       struct lpfc_rport_data *rdata = cmnd->device->hostdata;
+       struct lpfc_nodelist *ndlp = rdata->pnode;
+       struct lpfc_scsi_buf *lpfc_cmd = NULL;
+       struct list_head *scsi_buf_list = &phba->lpfc_scsi_buf_list;
+       int err = 0;
+
+       /*
+        * The target pointer is guaranteed not to be NULL because the driver
+        * only clears the device->hostdata field in lpfc_slave_destroy.  This
+        * approach guarantees no further IO calls on this target.
+        */
+       if (!ndlp) {
+               cmnd->result = ScsiResult(DID_NO_CONNECT, 0);
+               goto out_fail_command;
+       }
+
+       /*
+        * A Fibre Channel target is present and functioning only when the node
+        * state is MAPPED.  Any other state is a failure.
+        */
+       if (ndlp->nlp_state != NLP_STE_MAPPED_NODE) {
+               if ((ndlp->nlp_state == NLP_STE_UNMAPPED_NODE) ||
+                   (ndlp->nlp_state == NLP_STE_UNUSED_NODE)) {
+                       cmnd->result = ScsiResult(DID_NO_CONNECT, 0);
+                       goto out_fail_command;
+               }
+               /*
+                * The device is most likely recovered and the driver
+                * needs a bit more time to finish.  Ask the midlayer
+                * to retry.
+                */
+               goto out_host_busy;
+       }
+
+       list_remove_head(scsi_buf_list, lpfc_cmd, struct lpfc_scsi_buf, list);
+       if (lpfc_cmd == NULL) {
+               printk(KERN_WARNING "%s: No buffer available - list empty, "
+                      "total count %d\n", __FUNCTION__, phba->total_scsi_bufs);
+               goto out_host_busy;
+       }
+
+       /*
+        * Store the midlayer's command structure for the completion phase
+        * and complete the command initialization.
+        */
+       lpfc_cmd->pCmd  = cmnd;
+       lpfc_cmd->rdata = rdata;
+       lpfc_cmd->timeout = 0;
+       cmnd->host_scribble = (unsigned char *)lpfc_cmd;
+       cmnd->scsi_done = done;
+
+       err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
+       if (err)
+               goto out_host_busy_free_buf;
+
+       lpfc_scsi_prep_cmnd(phba, lpfc_cmd, ndlp);
+
+       err = lpfc_sli_issue_iocb(phba, &phba->sli.ring[psli->fcp_ring],
+                               &lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB);
+       if (err)
+               goto out_host_busy_free_buf;
+       return 0;
+
+ out_host_busy_free_buf:
+       lpfc_free_scsi_buf(lpfc_cmd);
+       cmnd->host_scribble = NULL;
+ out_host_busy:
+       return SCSI_MLQUEUE_HOST_BUSY;
+
+ out_fail_command:
+       done(cmnd);
+       return 0;
+}
+
+static int
+lpfc_abort_handler(struct scsi_cmnd *cmnd)
+{
+       struct lpfc_hba *phba =
+                       (struct lpfc_hba *)cmnd->device->host->hostdata[0];
+       struct lpfc_sli_ring *pring = &phba->sli.ring[phba->sli.fcp_ring];
+       struct lpfc_iocbq *iocb, *next_iocb;
+       struct lpfc_iocbq *abtsiocb = NULL;
+       struct lpfc_scsi_buf *lpfc_cmd;
+       struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list;
+       IOCB_t *cmd, *icmd;
+       unsigned long snum;
+       unsigned int id, lun;
+       unsigned int loop_count = 0;
+       int ret = IOCB_SUCCESS;
+
+       /*
+        * If the host_scribble data area is NULL, then the driver has already
+        * completed this command, but the midlayer did not see the completion
+        * before the eh fired.  Just return SUCCESS.
+        */
+       lpfc_cmd = (struct lpfc_scsi_buf *)cmnd->host_scribble;
+       if (!lpfc_cmd)
+               return SUCCESS;
+
+       /* save these now since lpfc_cmd can be freed */
+       id   = lpfc_cmd->pCmd->device->id;
+       lun  = lpfc_cmd->pCmd->device->lun;
+       snum = lpfc_cmd->pCmd->serial_number;
+
+       list_for_each_entry_safe(iocb, next_iocb, &pring->txq, list) {
+               cmd = &iocb->iocb;
+               if (iocb->context1 != lpfc_cmd)
+                       continue;
+
+               list_del_init(&iocb->list);
+               pring->txq_cnt--;
+               if (!iocb->iocb_cmpl) {
+                       list_add_tail(&iocb->list, lpfc_iocb_list);
+               }
+               else {
+                       cmd->ulpStatus = IOSTAT_LOCAL_REJECT;
+                       cmd->un.ulpWord[4] = IOERR_SLI_ABORTED;
+                       lpfc_scsi_cmd_iocb_cmpl_aborted(phba, iocb, iocb);
+               }
+
+               goto out;
+       }
+
+       list_remove_head(lpfc_iocb_list, abtsiocb, struct lpfc_iocbq, list);
+       if (abtsiocb == NULL)
+               return FAILED;
+
+       memset(abtsiocb, 0, sizeof (struct lpfc_iocbq));
+
+       /*
+        * The scsi command was not in the txq.  Check the txcmplq and if it is
+        * found, send an abort to the FW.
+        */
+       list_for_each_entry_safe(iocb, next_iocb, &pring->txcmplq, list) {
+               if (iocb->context1 != lpfc_cmd)
+                       continue;
+
+               iocb->iocb_cmpl = lpfc_scsi_cmd_iocb_cmpl_aborted;
+               cmd = &iocb->iocb;
+               icmd = &abtsiocb->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, abtsiocb, 0) ==
+                                                               IOCB_ERROR) {
+                       list_add_tail(&abtsiocb->list, lpfc_iocb_list);
+                       ret = IOCB_ERROR;
+                       break;
+               }
+
+               /* Wait for abort to complete */
+               while (cmnd->host_scribble)
+               {
+                       spin_unlock_irq(phba->host->host_lock);
+                       set_current_state(TASK_UNINTERRUPTIBLE);
+                       schedule_timeout(LPFC_ABORT_WAIT*HZ);
+                       spin_lock_irq(phba->host->host_lock);
+                       if (++loop_count
+                           > (2 * phba->cfg_nodev_tmo)/LPFC_ABORT_WAIT)
+                               break;
+               }
+
+               if(cmnd->host_scribble) {
+                       lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+                                       "%d:0748 abort handler timed "
+                                       "out waiting for abort to "
+                                       "complete. Data: "
+                                       "x%x x%x x%x x%lx\n",
+                                       phba->brd_no, ret, id, lun, snum);
+                       cmnd->host_scribble = NULL;
+                       iocb->iocb_cmpl = lpfc_scsi_cmd_iocb_cleanup;
+                       ret = IOCB_ERROR;
+               }
+
+               break;
+       }
+
+ out:
+       lpfc_printf_log(phba, KERN_WARNING, LOG_FCP,
+                       "%d:0749 SCSI layer issued abort device "
+                       "Data: x%x x%x x%x x%lx\n",
+                       phba->brd_no, ret, id, lun, snum);
+
+       return ret == IOCB_SUCCESS ? SUCCESS : FAILED;
+}
+
+static int
+lpfc_reset_lun_handler(struct scsi_cmnd *cmnd)
+{
+       struct Scsi_Host *shost = cmnd->device->host;
+       struct lpfc_hba *phba = (struct lpfc_hba *)shost->hostdata[0];
+       struct lpfc_sli *psli = &phba->sli;
+       struct lpfc_scsi_buf *lpfc_cmd = NULL;
+       struct list_head *scsi_buf_list = &phba->lpfc_scsi_buf_list;
+       struct list_head *lpfc_iocb_list = &phba->lpfc_iocb_list;
+       struct lpfc_iocbq *iocbq, *iocbqrsp = NULL;
+       struct lpfc_rport_data *rdata = cmnd->device->hostdata;
+       struct lpfc_nodelist *pnode = rdata->pnode;
+       int ret = FAILED;
+       int cnt, loopcnt;
+
+       /*
+        * If target is not in a MAPPED state, delay the reset until
+        * target is rediscovered or nodev timeout expires.
+        */
+       while ( 1 ) {
+               if (!pnode)
+                       break;
+
+               if (pnode->nlp_state != NLP_STE_MAPPED_NODE) {
+                       spin_unlock_irq(phba->host->host_lock);
+                       set_current_state(TASK_UNINTERRUPTIBLE);
+                       schedule_timeout( HZ/2);
+                       spin_lock_irq(phba->host->host_lock);
+               }
+               if ((pnode) && (pnode->nlp_state == NLP_STE_MAPPED_NODE))
+                       break;
+       }
+
+       list_remove_head(scsi_buf_list, lpfc_cmd, struct lpfc_scsi_buf, list);
+       if (lpfc_cmd == NULL)
+               goto out;
+
+       lpfc_cmd->pCmd = cmnd;
+       lpfc_cmd->timeout = 60;
+       lpfc_cmd->scsi_hba = phba;
+
+       ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, FCP_LUN_RESET);
+       if (!ret)
+               goto out_free_scsi_buf;
+
+       iocbq = &lpfc_cmd->cur_iocbq;
+
+       /* get a buffer for this IOCB command response */
+       list_remove_head(lpfc_iocb_list, iocbqrsp, struct lpfc_iocbq, list);
+       if (iocbqrsp == NULL)
+               goto out_free_scsi_buf;
+
+       memset(iocbqrsp, 0, sizeof (struct lpfc_iocbq));
+
+       iocbq->iocb_flag |= LPFC_IO_POLL;
+       iocbq->iocb_cmpl = lpfc_sli_wake_iocb_high_priority;
+
+       ret = lpfc_sli_issue_iocb_wait_high_priority(phba,
+                    &phba->sli.ring[psli->fcp_ring],
+                    iocbq, 0, iocbqrsp, 60);
+       if (ret == IOCB_SUCCESS)
+               ret = SUCCESS;
+
+       lpfc_cmd->result = iocbqrsp->iocb.un.ulpWord[4];
+       lpfc_cmd->status = iocbqrsp->iocb.ulpStatus;
+       if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT)
+               if (lpfc_cmd->result & IOERR_DRVR_MASK)
+                       lpfc_cmd->status = IOSTAT_DRIVER_REJECT;
+
+       /*
+        * All outstanding txcmplq I/Os should have been aborted by the target.
+        * Unfortunately, some targets do not abide by this forcing the driver
+        * to double check.
+        */
+       lpfc_sli_abort_iocb(phba, &phba->sli.ring[phba->sli.fcp_ring],
+                           cmnd->device->id, cmnd->device->lun, 0,
+                           LPFC_CTX_LUN);
+
+       loopcnt = 0;
+       while((cnt = lpfc_sli_sum_iocb(phba,
+                                      &phba->sli.ring[phba->sli.fcp_ring],
+                                      cmnd->device->id, cmnd->device->lun,
+                                      LPFC_CTX_LUN))) {
+               spin_unlock_irq(phba->host->host_lock);
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(LPFC_RESET_WAIT*HZ);
+               spin_lock_irq(phba->host->host_lock);
+
+               if (++loopcnt
+                   > (2 * phba->cfg_nodev_tmo)/LPFC_RESET_WAIT)
+                       break;
+       }
+
+       if (cnt) {
+               lpfc_printf_log(phba, KERN_INFO, LOG_FCP,
+                       "%d:0719 LUN Reset I/O flush failure: cnt x%x\n",
+                       phba->brd_no, cnt);
+       }
+
+       list_add_tail(&iocbqrsp->list, lpfc_iocb_list);
+
+out_free_scsi_buf:
+       lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+                       "%d:0713 SCSI layer issued LUN reset (%d, %d) "
+                       "Data: x%x x%x x%x\n",
+                       phba->brd_no, lpfc_cmd->pCmd->device->id,
+                       lpfc_cmd->pCmd->device->lun, ret, lpfc_cmd->status,
+                       lpfc_cmd->result);
+       lpfc_free_scsi_buf(lpfc_cmd);
+out:
+       return ret;
+}
+
+/*
+ * Note: midlayer calls this function with the host_lock held
+ */
+static int
+lpfc_reset_bus_handler(struct scsi_cmnd *cmnd)
+{
+       struct Scsi_Host *shost = cmnd->device->host;
+       struct lpfc_hba *phba = (struct lpfc_hba *)shost->hostdata[0];
+       struct lpfc_nodelist *ndlp = NULL;
+       int match;
+       int ret = FAILED, i, err_count = 0;
+       int cnt, loopcnt;
+       unsigned int midlayer_id = 0;
+       struct lpfc_scsi_buf * lpfc_cmd = NULL;
+       struct list_head *scsi_buf_list = &phba->lpfc_scsi_buf_list;
+
+       list_remove_head(scsi_buf_list, lpfc_cmd, struct lpfc_scsi_buf, list);
+       if (lpfc_cmd == NULL)
+               goto out;
+
+       /* The lpfc_cmd storage is reused.  Set all loop invariants. */
+       lpfc_cmd->timeout = 60;
+       lpfc_cmd->pCmd = cmnd;
+       lpfc_cmd->scsi_hba = phba;
+
+       /*
+        * Since the driver manages a single bus device, reset all
+        * targets known to the driver.  Should any target reset
+        * fail, this routine returns failure to the midlayer.
+        */
+       midlayer_id = cmnd->device->id;
+       for (i = 0; i < MAX_FCP_TARGET; i++) {
+               /* Search the mapped list for this target ID */
+               match = 0;
+               list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) {
+                       if ((i == ndlp->nlp_sid) && ndlp->rport) {
+                               match = 1;
+                               break;
+                       }
+               }
+               if (!match)
+                       continue;
+
+               lpfc_cmd->pCmd->device->id = i;
+               lpfc_cmd->pCmd->device->hostdata = ndlp->rport->dd_data;
+               ret = lpfc_scsi_tgt_reset(lpfc_cmd, phba);
+               if (ret != SUCCESS) {
+                       lpfc_printf_log(phba, KERN_INFO, LOG_FCP,
+                               "%d:0713 Bus Reset on target %d failed\n",
+                               phba->brd_no, i);
+                       err_count++;
+               }
+       }
+
+       cmnd->device->id = midlayer_id;
+       loopcnt = 0;
+       while((cnt = lpfc_sli_sum_iocb(phba,
+                               &phba->sli.ring[phba->sli.fcp_ring],
+                               0, 0, LPFC_CTX_HOST))) {
+               spin_unlock_irq(phba->host->host_lock);
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(LPFC_RESET_WAIT*HZ);
+               spin_lock_irq(phba->host->host_lock);
+
+               if (++loopcnt
+                   > (2 * phba->cfg_nodev_tmo)/LPFC_RESET_WAIT)
+                       break;
+       }
+
+       if (cnt) {
+               /* flush all outstanding commands on the host */
+               i = lpfc_sli_abort_iocb(phba,
+                               &phba->sli.ring[phba->sli.fcp_ring], 0, 0, 0,
+                               LPFC_CTX_HOST);
+
+               lpfc_printf_log(phba, KERN_INFO, LOG_FCP,
+                  "%d:0715 Bus Reset I/O flush failure: cnt x%x left x%x\n",
+                  phba->brd_no, cnt, i);
+       }
+
+       if (!err_count)
+               ret = SUCCESS;
+
+       lpfc_free_scsi_buf(lpfc_cmd);
+       lpfc_printf_log(phba,
+                       KERN_ERR,
+                       LOG_FCP,
+                       "%d:0714 SCSI layer issued Bus Reset Data: x%x\n",
+                       phba->brd_no, ret);
+out:
+       return ret;
+}
+
+static int
+lpfc_slave_alloc(struct scsi_device *sdev)
+{
+       struct lpfc_hba *phba = (struct lpfc_hba *)sdev->host->hostdata[0];
+       struct lpfc_nodelist *ndlp = NULL;
+       int match = 0;
+       struct lpfc_scsi_buf *scsi_buf = NULL;
+       uint32_t total = 0, i;
+       uint32_t num_to_alloc = 0;
+       unsigned long flags;
+       struct list_head *listp;
+       struct list_head *node_list[6];
+
+       /*
+        * Store the target pointer in the scsi_device hostdata pointer provided
+        * the driver has already discovered the target id.
+        */
+
+       /* Search the nlp lists other than unmap_list for this target ID */
+       node_list[0] = &phba->fc_npr_list;
+       node_list[1] = &phba->fc_nlpmap_list;
+       node_list[2] = &phba->fc_prli_list;
+       node_list[3] = &phba->fc_reglogin_list;
+       node_list[4] = &phba->fc_adisc_list;
+       node_list[5] = &phba->fc_plogi_list;
+
+       for (i = 0; i < 6 && !match; i++) {
+               listp = node_list[i];
+               if (list_empty(listp))
+                       continue;
+               list_for_each_entry(ndlp, listp, nlp_listp) {
+                       if ((sdev->id == ndlp->nlp_sid) && ndlp->rport) {
+                               match = 1;
+                               break;
+                       }
+               }
+       }
+
+       if (!match)
+               return -ENXIO;
+
+       sdev->hostdata = ndlp->rport->dd_data;
+
+       /*
+        * Populate the cmds_per_lun count scsi_bufs into this host's globally
+        * available list of scsi buffers.  Don't allocate more than the
+        * HBA limit conveyed to the midlayer via the host structure.  Note
+        * that this list of scsi bufs exists for the lifetime of the driver.
+        */
+       total = phba->total_scsi_bufs;
+       num_to_alloc = LPFC_CMD_PER_LUN;
+       if (total >= phba->cfg_hba_queue_depth) {
+               printk(KERN_WARNING "%s, At config limitation of "
+                      "%d allocated scsi_bufs\n", __FUNCTION__, total);
+               return 0;
+       } else if (total + num_to_alloc > phba->cfg_hba_queue_depth) {
+               num_to_alloc = phba->cfg_hba_queue_depth - total;
+       }
+
+       for (i = 0; i < num_to_alloc; i++) {
+               scsi_buf = lpfc_get_scsi_buf(phba);
+               if (!scsi_buf) {
+                       printk(KERN_ERR "%s, failed to allocate "
+                              "scsi_buf\n", __FUNCTION__);
+                       break;
+               }
+
+               spin_lock_irqsave(phba->host->host_lock, flags);
+               phba->total_scsi_bufs++;
+               list_add_tail(&scsi_buf->list, &phba->lpfc_scsi_buf_list);
+               spin_unlock_irqrestore(phba->host->host_lock, flags);
+       }
+       return 0;
+}
+
+static int
+lpfc_slave_configure(struct scsi_device *sdev)
+{
+       struct lpfc_hba *phba = (struct lpfc_hba *) sdev->host->hostdata[0];
+       struct fc_rport *rport = starget_to_rport(sdev->sdev_target);
+
+       if (sdev->tagged_supported)
+               scsi_activate_tcq(sdev, phba->cfg_lun_queue_depth);
+       else
+               scsi_deactivate_tcq(sdev, phba->cfg_lun_queue_depth);
+
+       /*
+        * Initialize the fc transport attributes for the target
+        * containing this scsi device.  Also note that the driver's
+        * target pointer is stored in the starget_data for the
+        * driver's sysfs entry point functions.
+        */
+       rport->dev_loss_tmo = phba->cfg_nodev_tmo + 5;
+
+       return 0;
+}
+
+static void
+lpfc_slave_destroy(struct scsi_device *sdev)
+{
+       sdev->hostdata = NULL;
+       return;
+}
+
+struct scsi_host_template lpfc_template = {
+       .module                 = THIS_MODULE,
+       .name                   = LPFC_DRIVER_NAME,
+       .info                   = lpfc_info,
+       .queuecommand           = lpfc_queuecommand,
+       .eh_abort_handler       = lpfc_abort_handler,
+       .eh_device_reset_handler= lpfc_reset_lun_handler,
+       .eh_bus_reset_handler   = lpfc_reset_bus_handler,
+       .slave_alloc            = lpfc_slave_alloc,
+       .slave_configure        = lpfc_slave_configure,
+       .slave_destroy          = lpfc_slave_destroy,
+       .this_id                = -1,
+       .sg_tablesize           = LPFC_SG_SEG_CNT,
+       .cmd_per_lun            = LPFC_CMD_PER_LUN,
+       .use_clustering         = ENABLE_CLUSTERING,
+       .shost_attrs            = lpfc_host_attrs,
+};
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
new file mode 100644 (file)
index 0000000..9bc1f15
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ *                  QLOGIC LINUX SOFTWARE
+ *
+ * QLogic ISP2x00 device driver for Linux 2.6.x
+ * Copyright (C) 2003-2005 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.
+ *
+ */
+#include "qla_def.h"
+
+#include <linux/vmalloc.h>
+#include <scsi/scsi_transport_fc.h>
+
+/* SYSFS attributes --------------------------------------------------------- */
+
+static ssize_t
+qla2x00_sysfs_read_fw_dump(struct kobject *kobj, char *buf, loff_t off,
+    size_t count)
+{
+       struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
+           struct device, kobj)));
+
+       if (ha->fw_dump_reading == 0)
+               return 0;
+       if (off > ha->fw_dump_buffer_len)
+               return 0;
+       if (off + count > ha->fw_dump_buffer_len)
+               count = ha->fw_dump_buffer_len - off;
+
+       memcpy(buf, &ha->fw_dump_buffer[off], count);
+
+       return (count);
+}
+
+static ssize_t
+qla2x00_sysfs_write_fw_dump(struct kobject *kobj, char *buf, loff_t off,
+    size_t count)
+{
+       struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
+           struct device, kobj)));
+       int reading;
+       uint32_t dump_size;
+
+       if (off != 0)
+               return (0);
+
+       reading = simple_strtol(buf, NULL, 10);
+       switch (reading) {
+       case 0:
+               if (ha->fw_dump_reading == 1) {
+                       qla_printk(KERN_INFO, ha,
+                           "Firmware dump cleared on (%ld).\n",
+                           ha->host_no);
+
+                       vfree(ha->fw_dump_buffer);
+                       free_pages((unsigned long)ha->fw_dump,
+                           ha->fw_dump_order);
+
+                       ha->fw_dump_reading = 0;
+                       ha->fw_dump_buffer = NULL;
+                       ha->fw_dump = NULL;
+               }
+               break;
+       case 1:
+               if (ha->fw_dump != NULL && !ha->fw_dump_reading) {
+                       ha->fw_dump_reading = 1;
+
+                       dump_size = FW_DUMP_SIZE_1M;
+                       if (ha->fw_memory_size < 0x20000) 
+                               dump_size = FW_DUMP_SIZE_128K;
+                       else if (ha->fw_memory_size < 0x80000) 
+                               dump_size = FW_DUMP_SIZE_512K;
+                       ha->fw_dump_buffer = (char *)vmalloc(dump_size);
+                       if (ha->fw_dump_buffer == NULL) {
+                               qla_printk(KERN_WARNING, ha,
+                                   "Unable to allocate memory for firmware "
+                                   "dump buffer (%d).\n", dump_size);
+
+                               ha->fw_dump_reading = 0;
+                               return (count);
+                       }
+                       qla_printk(KERN_INFO, ha,
+                           "Firmware dump ready for read on (%ld).\n",
+                           ha->host_no);
+                       memset(ha->fw_dump_buffer, 0, dump_size);
+                       if (IS_QLA2100(ha) || IS_QLA2200(ha))
+                               qla2100_ascii_fw_dump(ha);
+                       else
+                               qla2300_ascii_fw_dump(ha);
+                       ha->fw_dump_buffer_len = strlen(ha->fw_dump_buffer);
+               }
+               break;
+       }
+       return (count);
+}
+
+static struct bin_attribute sysfs_fw_dump_attr = {
+       .attr = {
+               .name = "fw_dump",
+               .mode = S_IRUSR | S_IWUSR,
+               .owner = THIS_MODULE,
+       },
+       .size = 0,
+       .read = qla2x00_sysfs_read_fw_dump,
+       .write = qla2x00_sysfs_write_fw_dump,
+};
+
+static ssize_t
+qla2x00_sysfs_read_nvram(struct kobject *kobj, char *buf, loff_t off,
+    size_t count)
+{
+       struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
+           struct device, kobj)));
+       uint16_t        *witer;
+       unsigned long   flags;
+       uint16_t        cnt;
+
+       if (!capable(CAP_SYS_ADMIN) || off != 0 || count != sizeof(nvram_t))
+               return 0;
+
+       /* Read NVRAM. */
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       qla2x00_lock_nvram_access(ha);
+       witer = (uint16_t *)buf;
+       for (cnt = 0; cnt < count / 2; cnt++) {
+               *witer = cpu_to_le16(qla2x00_get_nvram_word(ha,
+                   cnt+ha->nvram_base));
+               witer++;
+       }
+       qla2x00_unlock_nvram_access(ha);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       return (count);
+}
+
+static ssize_t
+qla2x00_sysfs_write_nvram(struct kobject *kobj, char *buf, loff_t off,
+    size_t count)
+{
+       struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj,
+           struct device, kobj)));
+       uint8_t         *iter;
+       uint16_t        *witer;
+       unsigned long   flags;
+       uint16_t        cnt;
+       uint8_t         chksum;
+
+       if (!capable(CAP_SYS_ADMIN) || off != 0 || count != sizeof(nvram_t))
+               return 0;
+
+       /* Checksum NVRAM. */
+       iter = (uint8_t *)buf;
+       chksum = 0;
+       for (cnt = 0; cnt < count - 1; cnt++)
+               chksum += *iter++;
+       chksum = ~chksum + 1;
+       *iter = chksum;
+
+       /* Write NVRAM. */
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       qla2x00_lock_nvram_access(ha);
+       qla2x00_release_nvram_protection(ha);
+       witer = (uint16_t *)buf;
+       for (cnt = 0; cnt < count / 2; cnt++) {
+               qla2x00_write_nvram_word(ha, cnt+ha->nvram_base,
+                   cpu_to_le16(*witer));
+               witer++;
+       }
+       qla2x00_unlock_nvram_access(ha);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       return (count);
+}
+
+static struct bin_attribute sysfs_nvram_attr = {
+       .attr = {
+               .name = "nvram",
+               .mode = S_IRUSR | S_IWUSR,
+               .owner = THIS_MODULE,
+       },
+       .size = sizeof(nvram_t),
+       .read = qla2x00_sysfs_read_nvram,
+       .write = qla2x00_sysfs_write_nvram,
+};
+
+void
+qla2x00_alloc_sysfs_attr(scsi_qla_host_t *ha)
+{
+       struct Scsi_Host *host = ha->host;
+
+       sysfs_create_bin_file(&host->shost_gendev.kobj, &sysfs_fw_dump_attr);
+       sysfs_create_bin_file(&host->shost_gendev.kobj, &sysfs_nvram_attr);
+}
+
+void
+qla2x00_free_sysfs_attr(scsi_qla_host_t *ha)
+{
+       struct Scsi_Host *host = ha->host;
+
+       sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_fw_dump_attr);
+       sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_nvram_attr);
+}
+
+/* Host attributes. */
+
+static void
+qla2x00_get_host_port_id(struct Scsi_Host *shost)
+{
+       scsi_qla_host_t *ha = to_qla_host(shost);
+
+       fc_host_port_id(shost) = ha->d_id.b.domain << 16 |
+           ha->d_id.b.area << 8 | ha->d_id.b.al_pa;
+}
+
+static void
+qla2x00_get_starget_node_name(struct scsi_target *starget)
+{
+       struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
+       scsi_qla_host_t *ha = to_qla_host(host);
+       fc_port_t *fcport;
+       uint64_t node_name = 0;
+
+       list_for_each_entry(fcport, &ha->fcports, list) {
+               if (starget->id == fcport->os_target_id) {
+                       node_name = *(uint64_t *)fcport->node_name;
+                       break;
+               }
+       }
+
+       fc_starget_node_name(starget) = be64_to_cpu(node_name);
+}
+
+static void
+qla2x00_get_starget_port_name(struct scsi_target *starget)
+{
+       struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
+       scsi_qla_host_t *ha = to_qla_host(host);
+       fc_port_t *fcport;
+       uint64_t port_name = 0;
+
+       list_for_each_entry(fcport, &ha->fcports, list) {
+               if (starget->id == fcport->os_target_id) {
+                       port_name = *(uint64_t *)fcport->port_name;
+                       break;
+               }
+       }
+
+       fc_starget_port_name(starget) = be64_to_cpu(port_name);
+}
+
+static void
+qla2x00_get_starget_port_id(struct scsi_target *starget)
+{
+       struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
+       scsi_qla_host_t *ha = to_qla_host(host);
+       fc_port_t *fcport;
+       uint32_t port_id = ~0U;
+
+       list_for_each_entry(fcport, &ha->fcports, list) {
+               if (starget->id == fcport->os_target_id) {
+                       port_id = fcport->d_id.b.domain << 16 |
+                           fcport->d_id.b.area << 8 | fcport->d_id.b.al_pa;
+                       break;
+               }
+       }
+
+       fc_starget_port_id(starget) = port_id;
+}
+
+static void
+qla2x00_get_rport_loss_tmo(struct fc_rport *rport)
+{
+       struct Scsi_Host *host = rport_to_shost(rport);
+       scsi_qla_host_t *ha = to_qla_host(host);
+
+       rport->dev_loss_tmo = ha->port_down_retry_count + 5;
+}
+
+static void
+qla2x00_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
+{
+       struct Scsi_Host *host = rport_to_shost(rport);
+       scsi_qla_host_t *ha = to_qla_host(host);
+
+       if (timeout)
+               ha->port_down_retry_count = timeout;
+       else
+               ha->port_down_retry_count = 1;
+
+       rport->dev_loss_tmo = ha->port_down_retry_count + 5;
+}
+
+struct fc_function_template qla2xxx_transport_functions = {
+
+       .show_host_node_name = 1,
+       .show_host_port_name = 1,
+       .get_host_port_id = qla2x00_get_host_port_id,
+       .show_host_port_id = 1,
+
+       .dd_fcrport_size = sizeof(struct fc_port *),
+
+       .get_starget_node_name = qla2x00_get_starget_node_name,
+       .show_starget_node_name = 1,
+       .get_starget_port_name = qla2x00_get_starget_port_name,
+       .show_starget_port_name = 1,
+       .get_starget_port_id  = qla2x00_get_starget_port_id,
+       .show_starget_port_id = 1,
+
+       .get_rport_dev_loss_tmo = qla2x00_get_rport_loss_tmo,
+       .set_rport_dev_loss_tmo = qla2x00_set_rport_loss_tmo,
+       .show_rport_dev_loss_tmo = 1,
+
+};
+
+void
+qla2x00_init_host_attr(scsi_qla_host_t *ha)
+{
+       fc_host_node_name(ha->host) =
+           be64_to_cpu(*(uint64_t *)ha->init_cb->node_name);
+       fc_host_port_name(ha->host) =
+           be64_to_cpu(*(uint64_t *)ha->init_cb->port_name);
+}
diff --git a/drivers/serial/ioc4_serial.c b/drivers/serial/ioc4_serial.c
new file mode 100644 (file)
index 0000000..ba4e13a
--- /dev/null
@@ -0,0 +1,2897 @@
+/*
+ * 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-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+
+/*
+ * This file contains a module version of the ioc4 serial driver. This
+ * includes all the support functions needed (support functions, etc.)
+ * and the serial driver itself.
+ */
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+#include <linux/circ_buf.h>
+#include <linux/serial_reg.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/ioc4_common.h>
+#include <linux/serial_core.h>
+
+/*
+ * interesting things about the ioc4
+ */
+
+#define IOC4_NUM_SERIAL_PORTS  4       /* max ports per card */
+#define IOC4_NUM_CARDS         8       /* max cards per partition */
+
+#define        GET_SIO_IR(_n)  (_n == 0) ? (IOC4_SIO_IR_S0) : \
+                               (_n == 1) ? (IOC4_SIO_IR_S1) : \
+                               (_n == 2) ? (IOC4_SIO_IR_S2) : \
+                               (IOC4_SIO_IR_S3)
+
+#define        GET_OTHER_IR(_n)  (_n == 0) ? (IOC4_OTHER_IR_S0_MEMERR) : \
+                               (_n == 1) ? (IOC4_OTHER_IR_S1_MEMERR) : \
+                               (_n == 2) ? (IOC4_OTHER_IR_S2_MEMERR) : \
+                               (IOC4_OTHER_IR_S3_MEMERR)
+
+
+/*
+ * All IOC4 registers are 32 bits wide.
+ */
+
+/*
+ * PCI Memory Space Map
+ */
+#define IOC4_PCI_ERR_ADDR_L     0x000  /* Low Error Address */
+#define IOC4_PCI_ERR_ADDR_VLD          (0x1 << 0)
+#define IOC4_PCI_ERR_ADDR_MST_ID_MSK    (0xf << 1)
+#define IOC4_PCI_ERR_ADDR_MST_NUM_MSK   (0xe << 1)
+#define IOC4_PCI_ERR_ADDR_MST_TYP_MSK   (0x1 << 1)
+#define IOC4_PCI_ERR_ADDR_MUL_ERR       (0x1 << 5)
+#define IOC4_PCI_ERR_ADDR_ADDR_MSK      (0x3ffffff << 6)
+
+/* Interrupt types */
+#define        IOC4_SIO_INTR_TYPE      0
+#define        IOC4_OTHER_INTR_TYPE    1
+#define        IOC4_NUM_INTR_TYPES     2
+
+/* Bitmasks for IOC4_SIO_IR, IOC4_SIO_IEC, and IOC4_SIO_IES  */
+#define IOC4_SIO_IR_S0_TX_MT      0x00000001   /* Serial port 0 TX empty */
+#define IOC4_SIO_IR_S0_RX_FULL    0x00000002   /* Port 0 RX buf full */
+#define IOC4_SIO_IR_S0_RX_HIGH    0x00000004   /* Port 0 RX hiwat */
+#define IOC4_SIO_IR_S0_RX_TIMER           0x00000008   /* Port 0 RX timeout */
+#define IOC4_SIO_IR_S0_DELTA_DCD   0x00000010  /* Port 0 delta DCD */
+#define IOC4_SIO_IR_S0_DELTA_CTS   0x00000020  /* Port 0 delta CTS */
+#define IOC4_SIO_IR_S0_INT        0x00000040   /* Port 0 pass-thru intr */
+#define IOC4_SIO_IR_S0_TX_EXPLICIT 0x00000080  /* Port 0 explicit TX thru */
+#define IOC4_SIO_IR_S1_TX_MT      0x00000100   /* Serial port 1 */
+#define IOC4_SIO_IR_S1_RX_FULL    0x00000200   /* */
+#define IOC4_SIO_IR_S1_RX_HIGH    0x00000400   /* */
+#define IOC4_SIO_IR_S1_RX_TIMER           0x00000800   /* */
+#define IOC4_SIO_IR_S1_DELTA_DCD   0x00001000  /* */
+#define IOC4_SIO_IR_S1_DELTA_CTS   0x00002000  /* */
+#define IOC4_SIO_IR_S1_INT        0x00004000   /* */
+#define IOC4_SIO_IR_S1_TX_EXPLICIT 0x00008000  /* */
+#define IOC4_SIO_IR_S2_TX_MT      0x00010000   /* Serial port 2 */
+#define IOC4_SIO_IR_S2_RX_FULL    0x00020000   /* */
+#define IOC4_SIO_IR_S2_RX_HIGH    0x00040000   /* */
+#define IOC4_SIO_IR_S2_RX_TIMER           0x00080000   /* */
+#define IOC4_SIO_IR_S2_DELTA_DCD   0x00100000  /* */
+#define IOC4_SIO_IR_S2_DELTA_CTS   0x00200000  /* */
+#define IOC4_SIO_IR_S2_INT        0x00400000   /* */
+#define IOC4_SIO_IR_S2_TX_EXPLICIT 0x00800000  /* */
+#define IOC4_SIO_IR_S3_TX_MT      0x01000000   /* Serial port 3 */
+#define IOC4_SIO_IR_S3_RX_FULL    0x02000000   /* */
+#define IOC4_SIO_IR_S3_RX_HIGH    0x04000000   /* */
+#define IOC4_SIO_IR_S3_RX_TIMER           0x08000000   /* */
+#define IOC4_SIO_IR_S3_DELTA_DCD   0x10000000  /* */
+#define IOC4_SIO_IR_S3_DELTA_CTS   0x20000000  /* */
+#define IOC4_SIO_IR_S3_INT        0x40000000   /* */
+#define IOC4_SIO_IR_S3_TX_EXPLICIT 0x80000000  /* */
+
+/* Per device interrupt masks */
+#define IOC4_SIO_IR_S0         (IOC4_SIO_IR_S0_TX_MT | \
+                                IOC4_SIO_IR_S0_RX_FULL | \
+                                IOC4_SIO_IR_S0_RX_HIGH | \
+                                IOC4_SIO_IR_S0_RX_TIMER | \
+                                IOC4_SIO_IR_S0_DELTA_DCD | \
+                                IOC4_SIO_IR_S0_DELTA_CTS | \
+                                IOC4_SIO_IR_S0_INT | \
+                                IOC4_SIO_IR_S0_TX_EXPLICIT)
+#define IOC4_SIO_IR_S1         (IOC4_SIO_IR_S1_TX_MT | \
+                                IOC4_SIO_IR_S1_RX_FULL | \
+                                IOC4_SIO_IR_S1_RX_HIGH | \
+                                IOC4_SIO_IR_S1_RX_TIMER | \
+                                IOC4_SIO_IR_S1_DELTA_DCD | \
+                                IOC4_SIO_IR_S1_DELTA_CTS | \
+                                IOC4_SIO_IR_S1_INT | \
+                                IOC4_SIO_IR_S1_TX_EXPLICIT)
+#define IOC4_SIO_IR_S2         (IOC4_SIO_IR_S2_TX_MT | \
+                                IOC4_SIO_IR_S2_RX_FULL | \
+                                IOC4_SIO_IR_S2_RX_HIGH | \
+                                IOC4_SIO_IR_S2_RX_TIMER | \
+                                IOC4_SIO_IR_S2_DELTA_DCD | \
+                                IOC4_SIO_IR_S2_DELTA_CTS | \
+                                IOC4_SIO_IR_S2_INT | \
+                                IOC4_SIO_IR_S2_TX_EXPLICIT)
+#define IOC4_SIO_IR_S3         (IOC4_SIO_IR_S3_TX_MT | \
+                                IOC4_SIO_IR_S3_RX_FULL | \
+                                IOC4_SIO_IR_S3_RX_HIGH | \
+                                IOC4_SIO_IR_S3_RX_TIMER | \
+                                IOC4_SIO_IR_S3_DELTA_DCD | \
+                                IOC4_SIO_IR_S3_DELTA_CTS | \
+                                IOC4_SIO_IR_S3_INT | \
+                                IOC4_SIO_IR_S3_TX_EXPLICIT)
+
+/* Bitmasks for IOC4_OTHER_IR, IOC4_OTHER_IEC, and IOC4_OTHER_IES  */
+#define IOC4_OTHER_IR_ATA_INT           0x00000001  /* ATAPI intr pass-thru */
+#define IOC4_OTHER_IR_ATA_MEMERR        0x00000002  /* ATAPI DMA PCI error */
+#define IOC4_OTHER_IR_S0_MEMERR         0x00000004  /* Port 0 PCI error */
+#define IOC4_OTHER_IR_S1_MEMERR         0x00000008  /* Port 1 PCI error */
+#define IOC4_OTHER_IR_S2_MEMERR         0x00000010  /* Port 2 PCI error */
+#define IOC4_OTHER_IR_S3_MEMERR         0x00000020  /* Port 3 PCI error */
+
+/* Bitmasks for IOC4_SIO_CR */
+#define IOC4_SIO_CR_CMD_PULSE_SHIFT              0  /* byte bus strobe shift */
+#define IOC4_SIO_CR_ARB_DIAG_TX0       0x00000000
+#define IOC4_SIO_CR_ARB_DIAG_RX0       0x00000010
+#define IOC4_SIO_CR_ARB_DIAG_TX1       0x00000020
+#define IOC4_SIO_CR_ARB_DIAG_RX1       0x00000030
+#define IOC4_SIO_CR_ARB_DIAG_TX2       0x00000040
+#define IOC4_SIO_CR_ARB_DIAG_RX2       0x00000050
+#define IOC4_SIO_CR_ARB_DIAG_TX3       0x00000060
+#define IOC4_SIO_CR_ARB_DIAG_RX3       0x00000070
+#define IOC4_SIO_CR_SIO_DIAG_IDLE      0x00000080  /* 0 -> active request among
+                                                          serial ports (ro) */
+/* Defs for some of the generic I/O pins */
+#define IOC4_GPCR_UART0_MODESEL           0x10 /* Pin is output to port 0
+                                                  mode sel */
+#define IOC4_GPCR_UART1_MODESEL           0x20 /* Pin is output to port 1
+                                                  mode sel */
+#define IOC4_GPCR_UART2_MODESEL           0x40 /* Pin is output to port 2
+                                                  mode sel */
+#define IOC4_GPCR_UART3_MODESEL           0x80 /* Pin is output to port 3
+                                                  mode sel */
+
+#define IOC4_GPPR_UART0_MODESEL_PIN   4        /* GIO pin controlling
+                                          uart 0 mode select */
+#define IOC4_GPPR_UART1_MODESEL_PIN   5        /* GIO pin controlling
+                                          uart 1 mode select */
+#define IOC4_GPPR_UART2_MODESEL_PIN   6        /* GIO pin controlling
+                                          uart 2 mode select */
+#define IOC4_GPPR_UART3_MODESEL_PIN   7        /* GIO pin controlling
+                                          uart 3 mode select */
+
+/* Bitmasks for serial RX status byte */
+#define IOC4_RXSB_OVERRUN       0x01   /* Char(s) lost */
+#define IOC4_RXSB_PAR_ERR      0x02    /* Parity error */
+#define IOC4_RXSB_FRAME_ERR    0x04    /* Framing error */
+#define IOC4_RXSB_BREAK                0x08    /* Break character */
+#define IOC4_RXSB_CTS          0x10    /* State of CTS */
+#define IOC4_RXSB_DCD          0x20    /* State of DCD */
+#define IOC4_RXSB_MODEM_VALID   0x40   /* DCD, CTS, and OVERRUN are valid */
+#define IOC4_RXSB_DATA_VALID    0x80   /* Data byte, FRAME_ERR PAR_ERR
+                                        * & BREAK valid */
+
+/* Bitmasks for serial TX control byte */
+#define IOC4_TXCB_INT_WHEN_DONE 0x20   /* Interrupt after this byte is sent */
+#define IOC4_TXCB_INVALID      0x00    /* Byte is invalid */
+#define IOC4_TXCB_VALID                0x40    /* Byte is valid */
+#define IOC4_TXCB_MCR          0x80    /* Data<7:0> to modem control reg */
+#define IOC4_TXCB_DELAY                0xc0    /* Delay data<7:0> mSec */
+
+/* Bitmasks for IOC4_SBBR_L */
+#define IOC4_SBBR_L_SIZE       0x00000001  /* 0 == 1KB rings, 1 == 4KB rings */
+
+/* Bitmasks for IOC4_SSCR_<3:0> */
+#define IOC4_SSCR_RX_THRESHOLD  0x000001ff  /* Hiwater mark */
+#define IOC4_SSCR_TX_TIMER_BUSY 0x00010000  /* TX timer in progress */
+#define IOC4_SSCR_HFC_EN       0x00020000  /* Hardware flow control enabled */
+#define IOC4_SSCR_RX_RING_DCD   0x00040000  /* Post RX record on delta-DCD */
+#define IOC4_SSCR_RX_RING_CTS   0x00080000  /* Post RX record on delta-CTS */
+#define IOC4_SSCR_DIAG         0x00200000  /* Bypass clock divider for sim */
+#define IOC4_SSCR_RX_DRAIN     0x08000000  /* Drain RX buffer to memory */
+#define IOC4_SSCR_DMA_EN       0x10000000  /* Enable ring buffer DMA */
+#define IOC4_SSCR_DMA_PAUSE    0x20000000  /* Pause DMA */
+#define IOC4_SSCR_PAUSE_STATE   0x40000000  /* Sets when PAUSE takes effect */
+#define IOC4_SSCR_RESET                0x80000000  /* Reset DMA channels */
+
+/* All producer/comsumer pointers are the same bitfield */
+#define IOC4_PROD_CONS_PTR_4K   0x00000ff8     /* For 4K buffers */
+#define IOC4_PROD_CONS_PTR_1K   0x000003f8     /* For 1K buffers */
+#define IOC4_PROD_CONS_PTR_OFF           3
+
+/* Bitmasks for IOC4_SRCIR_<3:0> */
+#define IOC4_SRCIR_ARM         0x80000000      /* Arm RX timer */
+
+/* Bitmasks for IOC4_SHADOW_<3:0> */
+#define IOC4_SHADOW_DR  0x00000001     /* Data ready */
+#define IOC4_SHADOW_OE  0x00000002     /* Overrun error */
+#define IOC4_SHADOW_PE  0x00000004     /* Parity error */
+#define IOC4_SHADOW_FE  0x00000008     /* Framing error */
+#define IOC4_SHADOW_BI  0x00000010     /* Break interrupt */
+#define IOC4_SHADOW_THRE 0x00000020    /* Xmit holding register empty */
+#define IOC4_SHADOW_TEMT 0x00000040    /* Xmit shift register empty */
+#define IOC4_SHADOW_RFCE 0x00000080    /* Char in RX fifo has an error */
+#define IOC4_SHADOW_DCTS 0x00010000    /* Delta clear to send */
+#define IOC4_SHADOW_DDCD 0x00080000    /* Delta data carrier detect */
+#define IOC4_SHADOW_CTS         0x00100000     /* Clear to send */
+#define IOC4_SHADOW_DCD         0x00800000     /* Data carrier detect */
+#define IOC4_SHADOW_DTR         0x01000000     /* Data terminal ready */
+#define IOC4_SHADOW_RTS         0x02000000     /* Request to send */
+#define IOC4_SHADOW_OUT1 0x04000000    /* 16550 OUT1 bit */
+#define IOC4_SHADOW_OUT2 0x08000000    /* 16550 OUT2 bit */
+#define IOC4_SHADOW_LOOP 0x10000000    /* Loopback enabled */
+
+/* Bitmasks for IOC4_SRTR_<3:0> */
+#define IOC4_SRTR_CNT          0x00000fff      /* Reload value for RX timer */
+#define IOC4_SRTR_CNT_VAL      0x0fff0000      /* Current value of RX timer */
+#define IOC4_SRTR_CNT_VAL_SHIFT         16
+#define IOC4_SRTR_HZ                 16000     /* SRTR clock frequency */
+
+/* Serial port register map used for DMA and PIO serial I/O */
+struct ioc4_serialregs {
+       uint32_t sscr;
+       uint32_t stpir;
+       uint32_t stcir;
+       uint32_t srpir;
+       uint32_t srcir;
+       uint32_t srtr;
+       uint32_t shadow;
+};
+
+/* IOC4 UART register map */
+struct ioc4_uartregs {
+       char i4u_lcr;
+       union {
+               char iir;       /* read only */
+               char fcr;       /* write only */
+       } u3;
+       union {
+               char ier;       /* DLAB == 0 */
+               char dlm;       /* DLAB == 1 */
+       } u2;
+       union {
+               char rbr;       /* read only, DLAB == 0 */
+               char thr;       /* write only, DLAB == 0 */
+               char dll;       /* DLAB == 1 */
+       } u1;
+       char i4u_scr;
+       char i4u_msr;
+       char i4u_lsr;
+       char i4u_mcr;
+};
+
+/* short names */
+#define i4u_dll u1.dll
+#define i4u_ier u2.ier
+#define i4u_dlm u2.dlm
+#define i4u_fcr u3.fcr
+
+/* PCI memory space register map addressed using pci_bar0 */
+struct ioc4_memregs {
+       struct ioc4_mem {
+               /* Miscellaneous IOC4  registers */
+               uint32_t pci_err_addr_l;
+               uint32_t pci_err_addr_h;
+               uint32_t sio_ir;
+               uint32_t other_ir;
+
+               /* These registers are read-only for general kernel code.  */
+               uint32_t sio_ies_ro;
+               uint32_t other_ies_ro;
+               uint32_t sio_iec_ro;
+               uint32_t other_iec_ro;
+               uint32_t sio_cr;
+               uint32_t misc_fill1;
+               uint32_t int_out;
+               uint32_t misc_fill2;
+               uint32_t gpcr_s;
+               uint32_t gpcr_c;
+               uint32_t gpdr;
+               uint32_t misc_fill3;
+               uint32_t gppr_0;
+               uint32_t gppr_1;
+               uint32_t gppr_2;
+               uint32_t gppr_3;
+               uint32_t gppr_4;
+               uint32_t gppr_5;
+               uint32_t gppr_6;
+               uint32_t gppr_7;
+       } ioc4_mem;
+
+       char misc_fill4[0x100 - 0x5C - 4];
+
+       /* ATA/ATAP registers */
+       uint32_t ata_notused[9];
+       char ata_fill1[0x140 - 0x120 - 4];
+       uint32_t ata_notused1[8];
+       char ata_fill2[0x200 - 0x15C - 4];
+
+       /* Keyboard and mouse registers */
+       uint32_t km_notused[5];;
+       char km_fill1[0x300 - 0x210 - 4];
+
+       /* Serial port registers used for DMA serial I/O */
+       struct ioc4_serial {
+               uint32_t sbbr01_l;
+               uint32_t sbbr01_h;
+               uint32_t sbbr23_l;
+               uint32_t sbbr23_h;
+
+               struct ioc4_serialregs port_0;
+               struct ioc4_serialregs port_1;
+               struct ioc4_serialregs port_2;
+               struct ioc4_serialregs port_3;
+               struct ioc4_uartregs uart_0;
+               struct ioc4_uartregs uart_1;
+               struct ioc4_uartregs uart_2;
+               struct ioc4_uartregs uart_3;
+       } ioc4_serial;
+};
+
+/* UART clock speed */
+#define IOC4_SER_XIN_CLK        IOC4_SER_XIN_CLK_66
+#define IOC4_SER_XIN_CLK_66     66666667
+#define IOC4_SER_XIN_CLK_33     33333333
+
+#define IOC4_W_IES             0
+#define IOC4_W_IEC             1
+
+typedef void ioc4_intr_func_f(void *, uint32_t);
+typedef ioc4_intr_func_f *ioc4_intr_func_t;
+
+/* defining this will get you LOTS of great debug info */
+//#define DEBUG_INTERRUPTS
+#define DPRINT_CONFIG(_x...)   ;
+//#define DPRINT_CONFIG(_x...) printk _x
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS   256
+
+/* number of characters we want to transmit to the lower level at a time */
+#define IOC4_MAX_CHARS 128
+
+/* Device name we're using */
+#define DEVICE_NAME    "ttyIOC"
+#define DEVICE_MAJOR 204
+#define DEVICE_MINOR 50
+
+/* register offsets */
+#define IOC4_SERIAL_OFFSET     0x300
+
+/* flags for next_char_state */
+#define NCS_BREAK      0x1
+#define NCS_PARITY     0x2
+#define NCS_FRAMING    0x4
+#define NCS_OVERRUN    0x8
+
+/* cause we need SOME parameters ... */
+#define MIN_BAUD_SUPPORTED     1200
+#define MAX_BAUD_SUPPORTED     115200
+
+/* protocol types supported */
+enum sio_proto {
+       PROTO_RS232,
+       PROTO_RS422
+};
+
+/* Notification types */
+#define N_DATA_READY   0x01
+#define N_OUTPUT_LOWAT 0x02
+#define N_BREAK                0x04
+#define N_PARITY_ERROR 0x08
+#define N_FRAMING_ERROR        0x10
+#define N_OVERRUN_ERROR        0x20
+#define N_DDCD         0x40
+#define N_DCTS         0x80
+
+#define N_ALL_INPUT    (N_DATA_READY | N_BREAK |                       \
+                        N_PARITY_ERROR | N_FRAMING_ERROR |             \
+                        N_OVERRUN_ERROR | N_DDCD | N_DCTS)
+
+#define N_ALL_OUTPUT   N_OUTPUT_LOWAT
+
+#define N_ALL_ERRORS   (N_PARITY_ERROR | N_FRAMING_ERROR | N_OVERRUN_ERROR)
+
+#define N_ALL          (N_DATA_READY | N_OUTPUT_LOWAT | N_BREAK |      \
+                        N_PARITY_ERROR | N_FRAMING_ERROR |             \
+                        N_OVERRUN_ERROR | N_DDCD | N_DCTS)
+
+#define SER_DIVISOR(_x, clk)           (((clk) + (_x) * 8) / ((_x) * 16))
+#define DIVISOR_TO_BAUD(div, clk)      ((clk) / 16 / (div))
+
+/* Some masks */
+#define LCR_MASK_BITS_CHAR     (UART_LCR_WLEN5 | UART_LCR_WLEN6 \
+                                       | UART_LCR_WLEN7 | UART_LCR_WLEN8)
+#define LCR_MASK_STOP_BITS     (UART_LCR_STOP)
+
+#define PENDING(_p)    (readl(&(_p)->ip_mem->sio_ir) & _p->ip_ienb)
+#define READ_SIO_IR(_p) readl(&(_p)->ip_mem->sio_ir)
+
+/* Default to 4k buffers */
+#ifdef IOC4_1K_BUFFERS
+#define RING_BUF_SIZE 1024
+#define IOC4_BUF_SIZE_BIT 0
+#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_1K
+#else
+#define RING_BUF_SIZE 4096
+#define IOC4_BUF_SIZE_BIT IOC4_SBBR_L_SIZE
+#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_4K
+#endif
+
+#define TOTAL_RING_BUF_SIZE (RING_BUF_SIZE * 4)
+
+/*
+ * This is the entry saved by the driver - one per card
+ */
+struct ioc4_control {
+       int ic_irq;
+       struct {
+               /* uart ports are allocated here */
+               struct uart_port icp_uart_port;
+               /* Handy reference material */
+               struct ioc4_port *icp_port;
+       } ic_port[IOC4_NUM_SERIAL_PORTS];
+       struct ioc4_soft *ic_soft;
+};
+
+/*
+ * per-IOC4 data structure
+ */
+#define MAX_IOC4_INTR_ENTS     (8 * sizeof(uint32_t))
+struct ioc4_soft {
+       struct ioc4_mem __iomem *is_ioc4_mem_addr;
+       struct ioc4_serial __iomem *is_ioc4_serial_addr;
+
+       /* Each interrupt type has an entry in the array */
+       struct ioc4_intr_type {
+
+               /*
+                * Each in-use entry in this array contains at least
+                * one nonzero bit in sd_bits; no two entries in this
+                * array have overlapping sd_bits values.
+                */
+               struct ioc4_intr_info {
+                       uint32_t sd_bits;
+                       ioc4_intr_func_f *sd_intr;
+                       void *sd_info;
+               } is_intr_info[MAX_IOC4_INTR_ENTS];
+
+               /* Number of entries active in the above array */
+               atomic_t is_num_intrs;
+       } is_intr_type[IOC4_NUM_INTR_TYPES];
+
+       /* is_ir_lock must be held while
+        * modifying sio_ie values, so
+        * we can be sure that sio_ie is
+        * not changing when we read it
+        * along with sio_ir.
+        */
+       spinlock_t is_ir_lock;  /* SIO_IE[SC] mod lock */
+};
+
+/* Local port info for each IOC4 serial ports */
+struct ioc4_port {
+       struct uart_port *ip_port;
+       /* Back ptrs for this port */
+       struct ioc4_control *ip_control;
+       struct pci_dev *ip_pdev;
+       struct ioc4_soft *ip_ioc4_soft;
+
+       /* pci mem addresses */
+       struct ioc4_mem __iomem *ip_mem;
+       struct ioc4_serial __iomem *ip_serial;
+       struct ioc4_serialregs __iomem *ip_serial_regs;
+       struct ioc4_uartregs __iomem *ip_uart_regs;
+
+       /* Ring buffer page for this port */
+       dma_addr_t ip_dma_ringbuf;
+       /* vaddr of ring buffer */
+       struct ring_buffer *ip_cpu_ringbuf;
+
+       /* Rings for this port */
+       struct ring *ip_inring;
+       struct ring *ip_outring;
+
+       /* Hook to port specific values */
+       struct hooks *ip_hooks;
+
+       spinlock_t ip_lock;
+
+       /* Various rx/tx parameters */
+       int ip_baud;
+       int ip_tx_lowat;
+       int ip_rx_timeout;
+
+       /* Copy of notification bits */
+       int ip_notify;
+
+       /* Shadow copies of various registers so we don't need to PIO
+        * read them constantly
+        */
+       uint32_t ip_ienb;       /* Enabled interrupts */
+       uint32_t ip_sscr;
+       uint32_t ip_tx_prod;
+       uint32_t ip_rx_cons;
+       int ip_pci_bus_speed;
+       unsigned char ip_flags;
+};
+
+/* tx low water mark.  We need to notify the driver whenever tx is getting
+ * close to empty so it can refill the tx buffer and keep things going.
+ * Let's assume that if we interrupt 1 ms before the tx goes idle, we'll
+ * have no trouble getting in more chars in time (I certainly hope so).
+ */
+#define TX_LOWAT_LATENCY      1000
+#define TX_LOWAT_HZ          (1000000 / TX_LOWAT_LATENCY)
+#define TX_LOWAT_CHARS(baud) (baud / 10 / TX_LOWAT_HZ)
+
+/* Flags per port */
+#define INPUT_HIGH     0x01
+#define DCD_ON         0x02
+#define LOWAT_WRITTEN  0x04
+#define READ_ABORTED   0x08
+
+/* Since each port has different register offsets and bitmasks
+ * for everything, we'll store those that we need in tables so we
+ * don't have to be constantly checking the port we are dealing with.
+ */
+struct hooks {
+       uint32_t intr_delta_dcd;
+       uint32_t intr_delta_cts;
+       uint32_t intr_tx_mt;
+       uint32_t intr_rx_timer;
+       uint32_t intr_rx_high;
+       uint32_t intr_tx_explicit;
+       uint32_t intr_dma_error;
+       uint32_t intr_clear;
+       uint32_t intr_all;
+       char rs422_select_pin;
+};
+
+static struct hooks hooks_array[IOC4_NUM_SERIAL_PORTS] = {
+       /* Values for port 0 */
+       {
+        IOC4_SIO_IR_S0_DELTA_DCD, IOC4_SIO_IR_S0_DELTA_CTS,
+        IOC4_SIO_IR_S0_TX_MT, IOC4_SIO_IR_S0_RX_TIMER,
+        IOC4_SIO_IR_S0_RX_HIGH, IOC4_SIO_IR_S0_TX_EXPLICIT,
+        IOC4_OTHER_IR_S0_MEMERR,
+        (IOC4_SIO_IR_S0_TX_MT | IOC4_SIO_IR_S0_RX_FULL |
+         IOC4_SIO_IR_S0_RX_HIGH | IOC4_SIO_IR_S0_RX_TIMER |
+         IOC4_SIO_IR_S0_DELTA_DCD | IOC4_SIO_IR_S0_DELTA_CTS |
+         IOC4_SIO_IR_S0_INT | IOC4_SIO_IR_S0_TX_EXPLICIT),
+        IOC4_SIO_IR_S0, IOC4_GPPR_UART0_MODESEL_PIN,
+        },
+
+       /* Values for port 1 */
+       {
+        IOC4_SIO_IR_S1_DELTA_DCD, IOC4_SIO_IR_S1_DELTA_CTS,
+        IOC4_SIO_IR_S1_TX_MT, IOC4_SIO_IR_S1_RX_TIMER,
+        IOC4_SIO_IR_S1_RX_HIGH, IOC4_SIO_IR_S1_TX_EXPLICIT,
+        IOC4_OTHER_IR_S1_MEMERR,
+        (IOC4_SIO_IR_S1_TX_MT | IOC4_SIO_IR_S1_RX_FULL |
+         IOC4_SIO_IR_S1_RX_HIGH | IOC4_SIO_IR_S1_RX_TIMER |
+         IOC4_SIO_IR_S1_DELTA_DCD | IOC4_SIO_IR_S1_DELTA_CTS |
+         IOC4_SIO_IR_S1_INT | IOC4_SIO_IR_S1_TX_EXPLICIT),
+        IOC4_SIO_IR_S1, IOC4_GPPR_UART1_MODESEL_PIN,
+        },
+
+       /* Values for port 2 */
+       {
+        IOC4_SIO_IR_S2_DELTA_DCD, IOC4_SIO_IR_S2_DELTA_CTS,
+        IOC4_SIO_IR_S2_TX_MT, IOC4_SIO_IR_S2_RX_TIMER,
+        IOC4_SIO_IR_S2_RX_HIGH, IOC4_SIO_IR_S2_TX_EXPLICIT,
+        IOC4_OTHER_IR_S2_MEMERR,
+        (IOC4_SIO_IR_S2_TX_MT | IOC4_SIO_IR_S2_RX_FULL |
+         IOC4_SIO_IR_S2_RX_HIGH | IOC4_SIO_IR_S2_RX_TIMER |
+         IOC4_SIO_IR_S2_DELTA_DCD | IOC4_SIO_IR_S2_DELTA_CTS |
+         IOC4_SIO_IR_S2_INT | IOC4_SIO_IR_S2_TX_EXPLICIT),
+        IOC4_SIO_IR_S2, IOC4_GPPR_UART2_MODESEL_PIN,
+        },
+
+       /* Values for port 3 */
+       {
+        IOC4_SIO_IR_S3_DELTA_DCD, IOC4_SIO_IR_S3_DELTA_CTS,
+        IOC4_SIO_IR_S3_TX_MT, IOC4_SIO_IR_S3_RX_TIMER,
+        IOC4_SIO_IR_S3_RX_HIGH, IOC4_SIO_IR_S3_TX_EXPLICIT,
+        IOC4_OTHER_IR_S3_MEMERR,
+        (IOC4_SIO_IR_S3_TX_MT | IOC4_SIO_IR_S3_RX_FULL |
+         IOC4_SIO_IR_S3_RX_HIGH | IOC4_SIO_IR_S3_RX_TIMER |
+         IOC4_SIO_IR_S3_DELTA_DCD | IOC4_SIO_IR_S3_DELTA_CTS |
+         IOC4_SIO_IR_S3_INT | IOC4_SIO_IR_S3_TX_EXPLICIT),
+        IOC4_SIO_IR_S3, IOC4_GPPR_UART3_MODESEL_PIN,
+        }
+};
+
+/* A ring buffer entry */
+struct ring_entry {
+       union {
+               struct {
+                       uint32_t alldata;
+                       uint32_t allsc;
+               } all;
+               struct {
+                       char data[4];   /* data bytes */
+                       char sc[4];     /* status/control */
+               } s;
+       } u;
+};
+
+/* Test the valid bits in any of the 4 sc chars using "allsc" member */
+#define RING_ANY_VALID \
+       ((uint32_t)(IOC4_RXSB_MODEM_VALID | IOC4_RXSB_DATA_VALID) * 0x01010101)
+
+#define ring_sc     u.s.sc
+#define ring_data   u.s.data
+#define ring_allsc  u.all.allsc
+
+/* Number of entries per ring buffer. */
+#define ENTRIES_PER_RING (RING_BUF_SIZE / (int) sizeof(struct ring_entry))
+
+/* An individual ring */
+struct ring {
+       struct ring_entry entries[ENTRIES_PER_RING];
+};
+
+/* The whole enchilada */
+struct ring_buffer {
+       struct ring TX_0_OR_2;
+       struct ring RX_0_OR_2;
+       struct ring TX_1_OR_3;
+       struct ring RX_1_OR_3;
+};
+
+/* Get a ring from a port struct */
+#define RING(_p, _wh)  &(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh)
+
+/* Infinite loop detection.
+ */
+#define MAXITER 10000000
+
+/* Prototypes */
+static void receive_chars(struct uart_port *);
+static void handle_intr(void *arg, uint32_t sio_ir);
+
+/**
+ * write_ireg - write the interrupt regs
+ * @ioc4_soft: ptr to soft struct for this port
+ * @val: value to write
+ * @which: which register
+ * @type: which ireg set
+ */
+static inline void
+write_ireg(struct ioc4_soft *ioc4_soft, uint32_t val, int which, int type)
+{
+       struct ioc4_mem __iomem *mem = ioc4_soft->is_ioc4_mem_addr;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ioc4_soft->is_ir_lock, flags);
+
+       switch (type) {
+       case IOC4_SIO_INTR_TYPE:
+               switch (which) {
+               case IOC4_W_IES:
+                       writel(val, &mem->sio_ies_ro);
+                       break;
+
+               case IOC4_W_IEC:
+                       writel(val, &mem->sio_iec_ro);
+                       break;
+               }
+               break;
+
+       case IOC4_OTHER_INTR_TYPE:
+               switch (which) {
+               case IOC4_W_IES:
+                       writel(val, &mem->other_ies_ro);
+                       break;
+
+               case IOC4_W_IEC:
+                       writel(val, &mem->other_iec_ro);
+                       break;
+               }
+               break;
+
+       default:
+               break;
+       }
+       spin_unlock_irqrestore(&ioc4_soft->is_ir_lock, flags);
+}
+
+/**
+ * set_baud - Baud rate setting code
+ * @port: port to set
+ * @baud: baud rate to use
+ */
+static int set_baud(struct ioc4_port *port, int baud)
+{
+       int actual_baud;
+       int diff;
+       int lcr;
+       unsigned short divisor;
+       struct ioc4_uartregs __iomem *uart;
+
+       divisor = SER_DIVISOR(baud, port->ip_pci_bus_speed);
+       if (!divisor)
+               return 1;
+       actual_baud = DIVISOR_TO_BAUD(divisor, port->ip_pci_bus_speed);
+
+       diff = actual_baud - baud;
+       if (diff < 0)
+               diff = -diff;
+
+       /* If we're within 1%, we've found a match */
+       if (diff * 100 > actual_baud)
+               return 1;
+
+       uart = port->ip_uart_regs;
+       lcr = readb(&uart->i4u_lcr);
+       writeb(lcr | UART_LCR_DLAB, &uart->i4u_lcr);
+       writeb((unsigned char)divisor, &uart->i4u_dll);
+       writeb((unsigned char)(divisor >> 8), &uart->i4u_dlm);
+       writeb(lcr, &uart->i4u_lcr);
+       return 0;
+}
+
+
+/**
+ * get_ioc4_port - given a uart port, return the control structure
+ * @port: uart port
+ */
+static struct ioc4_port *get_ioc4_port(struct uart_port *the_port)
+{
+       struct ioc4_control *control = dev_get_drvdata(the_port->dev);
+       int ii;
+
+       if (control) {
+               for ( ii = 0; ii < IOC4_NUM_SERIAL_PORTS; ii++ ) {
+                       if (!control->ic_port[ii].icp_port)
+                               continue;
+                       if (the_port == control->ic_port[ii].icp_port->ip_port)
+                               return control->ic_port[ii].icp_port;
+               }
+       }
+       return NULL;
+}
+
+/* The IOC4 hardware provides no atomic way to determine if interrupts
+ * are pending since two reads are required to do so.  The handler must
+ * read the SIO_IR and the SIO_IES, and take the logical and of the
+ * two.  When this value is zero, all interrupts have been serviced and
+ * the handler may return.
+ *
+ * This has the unfortunate "hole" that, if some other CPU or
+ * some other thread or some higher level interrupt manages to
+ * modify SIO_IE between our reads of SIO_IR and SIO_IE, we may
+ * think we have observed SIO_IR&SIO_IE==0 when in fact this
+ * condition never really occurred.
+ *
+ * To solve this, we use a simple spinlock that must be held
+ * whenever modifying SIO_IE; holding this lock while observing
+ * both SIO_IR and SIO_IE guarantees that we do not falsely
+ * conclude that no enabled interrupts are pending.
+ */
+
+static inline uint32_t
+pending_intrs(struct ioc4_soft *soft, int type)
+{
+       struct ioc4_mem __iomem *mem = soft->is_ioc4_mem_addr;
+       unsigned long flag;
+       uint32_t intrs = 0;
+
+       BUG_ON(!((type == IOC4_SIO_INTR_TYPE)
+              || (type == IOC4_OTHER_INTR_TYPE)));
+
+       spin_lock_irqsave(&soft->is_ir_lock, flag);
+
+       switch (type) {
+       case IOC4_SIO_INTR_TYPE:
+               intrs = readl(&mem->sio_ir) & readl(&mem->sio_ies_ro);
+               break;
+
+       case IOC4_OTHER_INTR_TYPE:
+               intrs = readl(&mem->other_ir) & readl(&mem->other_ies_ro);
+
+               /* Don't process any ATA interrupte */
+               intrs &= ~(IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR);
+               break;
+
+       default:
+               break;
+       }
+       spin_unlock_irqrestore(&soft->is_ir_lock, flag);
+       return intrs;
+}
+
+/**
+ * port_init - Initialize the sio and ioc4 hardware for a given port
+ *                     called per port from attach...
+ * @port: port to initialize
+ */
+static int inline port_init(struct ioc4_port *port)
+{
+       uint32_t sio_cr;
+       struct hooks *hooks = port->ip_hooks;
+       struct ioc4_uartregs __iomem *uart;
+
+       /* Idle the IOC4 serial interface */
+       writel(IOC4_SSCR_RESET, &port->ip_serial_regs->sscr);
+
+       /* Wait until any pending bus activity for this port has ceased */
+       do
+               sio_cr = readl(&port->ip_mem->sio_cr);
+       while (!(sio_cr & IOC4_SIO_CR_SIO_DIAG_IDLE));
+
+       /* Finish reset sequence */
+       writel(0, &port->ip_serial_regs->sscr);
+
+       /* Once RESET is done, reload cached tx_prod and rx_cons values
+        * and set rings to empty by making prod == cons
+        */
+       port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK;
+       writel(port->ip_tx_prod, &port->ip_serial_regs->stpir);
+       port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
+       writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir);
+
+       /* Disable interrupts for this 16550 */
+       uart = port->ip_uart_regs;
+       writeb(0, &uart->i4u_lcr);
+       writeb(0, &uart->i4u_ier);
+
+       /* Set the default baud */
+       set_baud(port, port->ip_baud);
+
+       /* Set line control to 8 bits no parity */
+       writeb(UART_LCR_WLEN8 | 0, &uart->i4u_lcr);
+                                       /* UART_LCR_STOP == 1 stop */
+
+       /* Enable the FIFOs */
+       writeb(UART_FCR_ENABLE_FIFO, &uart->i4u_fcr);
+       /* then reset 16550 FIFOs */
+       writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
+                       &uart->i4u_fcr);
+
+       /* Clear modem control register */
+       writeb(0, &uart->i4u_mcr);
+
+       /* Clear deltas in modem status register */
+       readb(&uart->i4u_msr);
+
+       /* Only do this once per port pair */
+       if (port->ip_hooks == &hooks_array[0]
+                           || port->ip_hooks == &hooks_array[2]) {
+               unsigned long ring_pci_addr;
+               uint32_t __iomem *sbbr_l;
+               uint32_t __iomem *sbbr_h;
+
+               if (port->ip_hooks == &hooks_array[0]) {
+                       sbbr_l = &port->ip_serial->sbbr01_l;
+                       sbbr_h = &port->ip_serial->sbbr01_h;
+               } else {
+                       sbbr_l = &port->ip_serial->sbbr23_l;
+                       sbbr_h = &port->ip_serial->sbbr23_h;
+               }
+
+               ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf;
+               DPRINT_CONFIG(("%s: ring_pci_addr 0x%lx\n",
+                                       __FUNCTION__, ring_pci_addr));
+
+               writel((unsigned int)((uint64_t)ring_pci_addr >> 32), sbbr_h);
+               writel((unsigned int)ring_pci_addr | IOC4_BUF_SIZE_BIT, sbbr_l);
+       }
+
+       /* Set the receive timeout value to 10 msec */
+       writel(IOC4_SRTR_HZ / 100, &port->ip_serial_regs->srtr);
+
+       /* Set rx threshold, enable DMA */
+       /* Set high water mark at 3/4 of full ring */
+       port->ip_sscr = (ENTRIES_PER_RING * 3 / 4);
+       writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+
+       /* Disable and clear all serial related interrupt bits */
+       write_ireg(port->ip_ioc4_soft, hooks->intr_clear,
+                      IOC4_W_IEC, IOC4_SIO_INTR_TYPE);
+       port->ip_ienb &= ~hooks->intr_clear;
+       writel(hooks->intr_clear, &port->ip_mem->sio_ir);
+       return 0;
+}
+
+/**
+ * handle_dma_error_intr - service any pending DMA error interrupts for the
+ *                     given port - 2nd level called via sd_intr
+ * @arg: handler arg
+ * @other_ir: ioc4regs
+ */
+static void handle_dma_error_intr(void *arg, uint32_t other_ir)
+{
+       struct ioc4_port *port = (struct ioc4_port *)arg;
+       struct hooks *hooks = port->ip_hooks;
+       unsigned int flags;
+
+       spin_lock_irqsave(&port->ip_lock, flags);
+
+       /* ACK the interrupt */
+       writel(hooks->intr_dma_error, &port->ip_mem->other_ir);
+
+       if (readl(&port->ip_mem->pci_err_addr_l) & IOC4_PCI_ERR_ADDR_VLD) {
+               printk(KERN_ERR
+                       "PCI error address is 0x%lx, "
+                               "master is serial port %c %s\n",
+                    (((uint64_t)readl(&port->ip_mem->pci_err_addr_h)
+                                                        << 32)
+                               | readl(&port->ip_mem->pci_err_addr_l))
+                                       & IOC4_PCI_ERR_ADDR_ADDR_MSK, '1' +
+                    ((char)(readl(&port->ip_mem-> pci_err_addr_l) &
+                            IOC4_PCI_ERR_ADDR_MST_NUM_MSK) >> 1),
+                    (readl(&port->ip_mem->pci_err_addr_l)
+                               & IOC4_PCI_ERR_ADDR_MST_TYP_MSK)
+                               ? "RX" : "TX");
+
+               if (readl(&port->ip_mem->pci_err_addr_l)
+                                               & IOC4_PCI_ERR_ADDR_MUL_ERR) {
+                       printk(KERN_ERR
+                               "Multiple errors occurred\n");
+               }
+       }
+       spin_unlock_irqrestore(&port->ip_lock, flags);
+
+       /* Re-enable DMA error interrupts */
+       write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error, IOC4_W_IES,
+                                               IOC4_OTHER_INTR_TYPE);
+}
+
+/**
+ * intr_connect - interrupt connect function
+ * @soft: soft struct for this card
+ * @type: interrupt type
+ * @intrbits: bit pattern to set
+ * @intr: handler function
+ * @info: handler arg
+ */
+static void
+intr_connect(struct ioc4_soft *soft, int type,
+                 uint32_t intrbits, ioc4_intr_func_f * intr, void *info)
+{
+       int i;
+       struct ioc4_intr_info *intr_ptr;
+
+       BUG_ON(!((type == IOC4_SIO_INTR_TYPE)
+              || (type == IOC4_OTHER_INTR_TYPE)));
+
+       i = atomic_inc(&soft-> is_intr_type[type].is_num_intrs) - 1;
+       BUG_ON(!(i < MAX_IOC4_INTR_ENTS || (printk("i %d\n", i), 0)));
+
+       /* Save off the lower level interrupt handler */
+       intr_ptr = &soft->is_intr_type[type].is_intr_info[i];
+       intr_ptr->sd_bits = intrbits;
+       intr_ptr->sd_intr = intr;
+       intr_ptr->sd_info = info;
+}
+
+/**
+ * ioc4_intr - Top level IOC4 interrupt handler.
+ * @irq: irq value
+ * @arg: handler arg
+ * @regs: registers
+ */
+static irqreturn_t ioc4_intr(int irq, void *arg, struct pt_regs *regs)
+{
+       struct ioc4_soft *soft;
+       uint32_t this_ir, this_mir;
+       int xx, num_intrs = 0;
+       int intr_type;
+       int handled = 0;
+       struct ioc4_intr_info *ii;
+
+       soft = arg;
+       for (intr_type = 0; intr_type < IOC4_NUM_INTR_TYPES; intr_type++) {
+               num_intrs = (int)atomic_read(
+                               &soft->is_intr_type[intr_type].is_num_intrs);
+
+               this_mir = this_ir = pending_intrs(soft, intr_type);
+
+               /* Farm out the interrupt to the various drivers depending on
+                * which interrupt bits are set.
+                */
+               for (xx = 0; xx < num_intrs; xx++) {
+                       ii = &soft->is_intr_type[intr_type].is_intr_info[xx];
+                       if ((this_mir = this_ir & ii->sd_bits)) {
+                               /* Disable owned interrupts, call handler */
+                               handled++;
+                               write_ireg(soft, ii->sd_bits, IOC4_W_IEC,
+                                                               intr_type);
+                               ii->sd_intr(ii->sd_info, this_mir);
+                               this_ir &= ~this_mir;
+                       }
+               }
+               if (this_ir) {
+                       printk(KERN_ERR
+                              "unknown IOC4 %s interrupt 0x%x, sio_ir = 0x%x,"
+                               " sio_ies = 0x%x, other_ir = 0x%x :"
+                               "other_ies = 0x%x\n",
+                              (intr_type == IOC4_SIO_INTR_TYPE) ? "sio" :
+                              "other", this_ir,
+                              readl(&soft->is_ioc4_mem_addr->sio_ir),
+                              readl(&soft->is_ioc4_mem_addr->sio_ies_ro),
+                              readl(&soft->is_ioc4_mem_addr->other_ir),
+                              readl(&soft->is_ioc4_mem_addr->other_ies_ro));
+               }
+       }
+#ifdef DEBUG_INTERRUPTS
+       {
+               struct ioc4_mem __iomem *mem = soft->is_ioc4_mem_addr;
+               spinlock_t *lp = &soft->is_ir_lock;
+               unsigned long flag;
+
+               spin_lock_irqsave(&soft->is_ir_lock, flag);
+               printk ("%s : %d : mem 0x%p sio_ir 0x%x sio_ies_ro 0x%x "
+                               "other_ir 0x%x other_ies_ro 0x%x mask 0x%x\n",
+                    __FUNCTION__, __LINE__,
+                    (void *)mem, readl(&mem->sio_ir),
+                    readl(&mem->sio_ies_ro),
+                    readl(&mem->other_ir),
+                    readl(&mem->other_ies_ro),
+                    IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR);
+               spin_unlock_irqrestore(&soft->is_ir_lock, flag);
+       }
+#endif
+       return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/**
+ * ioc4_attach_local - Device initialization.
+ *                     Called at *_attach() time for each
+ *                     IOC4 with serial ports in the system.
+ * @control: ioc4_control ptr
+ * @pdev: PCI handle for this device
+ * @soft: soft struct for this device
+ * @ioc4: ioc4 mem space
+ */
+static int inline ioc4_attach_local(struct pci_dev *pdev,
+                       struct ioc4_control *control,
+                       struct ioc4_soft *soft, void __iomem *ioc4_mem,
+                       void __iomem *ioc4_serial)
+{
+       struct ioc4_port *port;
+       struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS];
+       int port_number;
+       uint16_t ioc4_revid_min = 62;
+       uint16_t ioc4_revid;
+
+       /* IOC4 firmware must be at least rev 62 */
+       pci_read_config_word(pdev, PCI_COMMAND_SPECIAL, &ioc4_revid);
+
+       printk(KERN_INFO "IOC4 firmware revision %d\n", ioc4_revid);
+       if (ioc4_revid < ioc4_revid_min) {
+               printk(KERN_WARNING
+                   "IOC4 serial not supported on firmware rev %d, "
+                               "please upgrade to rev %d or higher\n",
+                               ioc4_revid, ioc4_revid_min);
+               return -EPERM;
+       }
+       BUG_ON(ioc4_mem == NULL);
+       BUG_ON(ioc4_serial == NULL);
+
+       /* Create port structures for each port */
+       for (port_number = 0; port_number < IOC4_NUM_SERIAL_PORTS;
+                                                       port_number++) {
+               port = kmalloc(sizeof(struct ioc4_port), GFP_KERNEL);
+               if (!port) {
+                       printk(KERN_WARNING
+                               "IOC4 serial memory not available for port\n");
+                       return -ENOMEM;
+               }
+               memset(port, 0, sizeof(struct ioc4_port));
+
+               /* we need to remember the previous ones, to point back to
+                * them farther down - setting up the ring buffers.
+                */
+               ports[port_number] = port;
+
+               /* Allocate buffers and jumpstart the hardware.  */
+               control->ic_port[port_number].icp_port = port;
+               port->ip_ioc4_soft = soft;
+               port->ip_pdev = pdev;
+               port->ip_ienb = 0;
+               port->ip_pci_bus_speed = IOC4_SER_XIN_CLK;
+               port->ip_baud = 9600;
+               port->ip_control = control;
+               port->ip_mem = ioc4_mem;
+               port->ip_serial = ioc4_serial;
+
+               /* point to the right hook */
+               port->ip_hooks = &hooks_array[port_number];
+
+               /* Get direct hooks to the serial regs and uart regs
+                * for this port
+                */
+               switch (port_number) {
+               case 0:
+                       port->ip_serial_regs = &(port->ip_serial->port_0);
+                       port->ip_uart_regs = &(port->ip_serial->uart_0);
+                       break;
+               case 1:
+                       port->ip_serial_regs = &(port->ip_serial->port_1);
+                       port->ip_uart_regs = &(port->ip_serial->uart_1);
+                       break;
+               case 2:
+                       port->ip_serial_regs = &(port->ip_serial->port_2);
+                       port->ip_uart_regs = &(port->ip_serial->uart_2);
+                       break;
+               default:
+               case 3:
+                       port->ip_serial_regs = &(port->ip_serial->port_3);
+                       port->ip_uart_regs = &(port->ip_serial->uart_3);
+                       break;
+               }
+
+               /* ring buffers are 1 to a pair of ports */
+               if (port_number && (port_number & 1)) {
+                       /* odd use the evens buffer */
+                       port->ip_dma_ringbuf =
+                                       ports[port_number - 1]->ip_dma_ringbuf;
+                       port->ip_cpu_ringbuf =
+                                       ports[port_number - 1]->ip_cpu_ringbuf;
+                       port->ip_inring = RING(port, RX_1_OR_3);
+                       port->ip_outring = RING(port, TX_1_OR_3);
+
+               } else {
+                       if (port->ip_dma_ringbuf == 0) {
+                               port->ip_cpu_ringbuf = pci_alloc_consistent
+                                       (pdev, TOTAL_RING_BUF_SIZE,
+                                       &port->ip_dma_ringbuf);
+
+                       }
+                       BUG_ON(!((((int64_t)port->ip_dma_ringbuf) &
+                               (TOTAL_RING_BUF_SIZE - 1)) == 0));
+                       DPRINT_CONFIG(("%s : ip_cpu_ringbuf 0x%p "
+                                               "ip_dma_ringbuf 0x%p\n",
+                                       __FUNCTION__,
+                                       (void *)port->ip_cpu_ringbuf,
+                                       (void *)port->ip_dma_ringbuf));
+                       port->ip_inring = RING(port, RX_0_OR_2);
+                       port->ip_outring = RING(port, TX_0_OR_2);
+               }
+               DPRINT_CONFIG(("%s : port %d [addr 0x%p] control 0x%p",
+                               __FUNCTION__,
+                               port_number, (void *)port, (void *)control));
+               DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n",
+                               (void *)port->ip_serial_regs,
+                               (void *)port->ip_uart_regs));
+
+               /* Initialize the hardware for IOC4 */
+               port_init(port);
+
+               DPRINT_CONFIG(("%s: port_number %d port 0x%p inring 0x%p "
+                                               "outring 0x%p\n",
+                               __FUNCTION__,
+                               port_number, (void *)port,
+                               (void *)port->ip_inring,
+                               (void *)port->ip_outring));
+
+               /* Attach interrupt handlers */
+               intr_connect(soft, IOC4_SIO_INTR_TYPE,
+                               GET_SIO_IR(port_number),
+                               handle_intr, port);
+
+               intr_connect(soft, IOC4_OTHER_INTR_TYPE,
+                               GET_OTHER_IR(port_number),
+                               handle_dma_error_intr, port);
+       }
+       return 0;
+}
+
+/**
+ * enable_intrs - enable interrupts
+ * @port: port to enable
+ * @mask: mask to use
+ */
+static void enable_intrs(struct ioc4_port *port, uint32_t mask)
+{
+       struct hooks *hooks = port->ip_hooks;
+
+       if ((port->ip_ienb & mask) != mask) {
+               write_ireg(port->ip_ioc4_soft, mask, IOC4_W_IES,
+                                               IOC4_SIO_INTR_TYPE);
+               port->ip_ienb |= mask;
+       }
+
+       if (port->ip_ienb)
+               write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error,
+                               IOC4_W_IES, IOC4_OTHER_INTR_TYPE);
+}
+
+/**
+ * local_open - local open a port
+ * @port: port to open
+ */
+static inline int local_open(struct ioc4_port *port)
+{
+       int spiniter = 0;
+
+       port->ip_flags = 0;
+
+       /* Pause the DMA interface if necessary */
+       if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
+               writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE,
+                       &port->ip_serial_regs->sscr);
+               while((readl(&port->ip_serial_regs-> sscr)
+                               & IOC4_SSCR_PAUSE_STATE) == 0) {
+                       spiniter++;
+                       if (spiniter > MAXITER) {
+                               return -1;
+                       }
+               }
+       }
+
+       /* Reset the input fifo.  If the uart received chars while the port
+        * was closed and DMA is not enabled, the uart may have a bunch of
+        * chars hanging around in its rx fifo which will not be discarded
+        * by rclr in the upper layer. We must get rid of them here.
+        */
+       writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR,
+                               &port->ip_uart_regs->i4u_fcr);
+
+       writeb(UART_LCR_WLEN8, &port->ip_uart_regs->i4u_lcr);
+                                       /* UART_LCR_STOP == 1 stop */
+
+       /* Re-enable DMA, set default threshold to intr whenever there is
+        * data available.
+        */
+       port->ip_sscr &= ~IOC4_SSCR_RX_THRESHOLD;
+       port->ip_sscr |= 1;     /* default threshold */
+
+       /* Plug in the new sscr.  This implicitly clears the DMA_PAUSE
+        * flag if it was set above
+        */
+       writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+       port->ip_tx_lowat = 1;
+       return 0;
+}
+
+/**
+ * set_rx_timeout - Set rx timeout and threshold values.
+ * @port: port to use
+ * @timeout: timeout value in ticks
+ */
+static inline int set_rx_timeout(struct ioc4_port *port, int timeout)
+{
+       int threshold;
+
+       port->ip_rx_timeout = timeout;
+
+       /* Timeout is in ticks.  Let's figure out how many chars we
+        * can receive at the current baud rate in that interval
+        * and set the rx threshold to that amount.  There are 4 chars
+        * per ring entry, so we'll divide the number of chars that will
+        * arrive in timeout by 4.
+        * So .... timeout * baud / 10 / HZ / 4, with HZ = 100.
+        */
+       threshold = timeout * port->ip_baud / 4000;
+       if (threshold == 0)
+               threshold = 1;  /* otherwise we'll intr all the time! */
+
+       if ((unsigned)threshold > (unsigned)IOC4_SSCR_RX_THRESHOLD)
+               return 1;
+
+       port->ip_sscr &= ~IOC4_SSCR_RX_THRESHOLD;
+       port->ip_sscr |= threshold;
+
+       writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+
+       /* Now set the rx timeout to the given value
+        * again timeout * IOC4_SRTR_HZ / HZ
+        */
+       timeout = timeout * IOC4_SRTR_HZ / 100;
+       if (timeout > IOC4_SRTR_CNT)
+               timeout = IOC4_SRTR_CNT;
+
+       writel(timeout, &port->ip_serial_regs->srtr);
+       return 0;
+}
+
+/**
+ * config_port - config the hardware
+ * @port: port to config
+ * @baud: baud rate for the port
+ * @byte_size: data size
+ * @stop_bits: number of stop bits
+ * @parenb: parity enable ?
+ * @parodd: odd parity ?
+ */
+static inline int
+config_port(struct ioc4_port *port,
+           int baud, int byte_size, int stop_bits, int parenb, int parodd)
+{
+       char lcr, sizebits;
+       int spiniter = 0;
+
+       DPRINT_CONFIG(("%s: baud %d byte_size %d stop %d parenb %d parodd %d\n",
+               __FUNCTION__, baud, byte_size, stop_bits, parenb, parodd));
+
+       if (set_baud(port, baud))
+               return 1;
+
+       switch (byte_size) {
+       case 5:
+               sizebits = UART_LCR_WLEN5;
+               break;
+       case 6:
+               sizebits = UART_LCR_WLEN6;
+               break;
+       case 7:
+               sizebits = UART_LCR_WLEN7;
+               break;
+       case 8:
+               sizebits = UART_LCR_WLEN8;
+               break;
+       default:
+               return 1;
+       }
+
+       /* Pause the DMA interface if necessary */
+       if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
+               writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE,
+                       &port->ip_serial_regs->sscr);
+               while((readl(&port->ip_serial_regs->sscr)
+                                               & IOC4_SSCR_PAUSE_STATE) == 0) {
+                       spiniter++;
+                       if (spiniter > MAXITER)
+                               return -1;
+               }
+       }
+
+       /* Clear relevant fields in lcr */
+       lcr = readb(&port->ip_uart_regs->i4u_lcr);
+       lcr &= ~(LCR_MASK_BITS_CHAR | UART_LCR_EPAR |
+                UART_LCR_PARITY | LCR_MASK_STOP_BITS);
+
+       /* Set byte size in lcr */
+       lcr |= sizebits;
+
+       /* Set parity */
+       if (parenb) {
+               lcr |= UART_LCR_PARITY;
+               if (!parodd)
+                       lcr |= UART_LCR_EPAR;
+       }
+
+       /* Set stop bits */
+       if (stop_bits)
+               lcr |= UART_LCR_STOP /* 2 stop bits */ ;
+
+       writeb(lcr, &port->ip_uart_regs->i4u_lcr);
+
+       /* Re-enable the DMA interface if necessary */
+       if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
+               writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+       }
+       port->ip_baud = baud;
+
+       /* When we get within this number of ring entries of filling the
+        * entire ring on tx, place an EXPLICIT intr to generate a lowat
+        * notification when output has drained.
+        */
+       port->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4;
+       if (port->ip_tx_lowat == 0)
+               port->ip_tx_lowat = 1;
+
+       set_rx_timeout(port, 2);
+
+       return 0;
+}
+
+/**
+ * do_write - Write bytes to the port.  Returns the number of bytes
+ *                     actually written. Called from transmit_chars
+ * @port: port to use
+ * @buf: the stuff to write
+ * @len: how many bytes in 'buf'
+ */
+static inline int do_write(struct ioc4_port *port, char *buf, int len)
+{
+       int prod_ptr, cons_ptr, total = 0;
+       struct ring *outring;
+       struct ring_entry *entry;
+       struct hooks *hooks = port->ip_hooks;
+
+       BUG_ON(!(len >= 0));
+
+       prod_ptr = port->ip_tx_prod;
+       cons_ptr = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK;
+       outring = port->ip_outring;
+
+       /* Maintain a 1-entry red-zone.  The ring buffer is full when
+        * (cons - prod) % ring_size is 1.  Rather than do this subtraction
+        * in the body of the loop, I'll do it now.
+        */
+       cons_ptr = (cons_ptr - (int)sizeof(struct ring_entry)) & PROD_CONS_MASK;
+
+       /* Stuff the bytes into the output */
+       while ((prod_ptr != cons_ptr) && (len > 0)) {
+               int xx;
+
+               /* Get 4 bytes (one ring entry) at a time */
+               entry = (struct ring_entry *)((caddr_t) outring + prod_ptr);
+
+               /* Invalidate all entries */
+               entry->ring_allsc = 0;
+
+               /* Copy in some bytes */
+               for (xx = 0; (xx < 4) && (len > 0); xx++) {
+                       entry->ring_data[xx] = *buf++;
+                       entry->ring_sc[xx] = IOC4_TXCB_VALID;
+                       len--;
+                       total++;
+               }
+
+               /* If we are within some small threshold of filling up the
+                * entire ring buffer, we must place an EXPLICIT intr here
+                * to generate a lowat interrupt in case we subsequently
+                * really do fill up the ring and the caller goes to sleep.
+                * No need to place more than one though.
+                */
+               if (!(port->ip_flags & LOWAT_WRITTEN) &&
+                       ((cons_ptr - prod_ptr) & PROD_CONS_MASK)
+                               <= port->ip_tx_lowat
+                                       * (int)sizeof(struct ring_entry)) {
+                       port->ip_flags |= LOWAT_WRITTEN;
+                       entry->ring_sc[0] |= IOC4_TXCB_INT_WHEN_DONE;
+               }
+
+               /* Go on to next entry */
+               prod_ptr += sizeof(struct ring_entry);
+               prod_ptr &= PROD_CONS_MASK;
+       }
+
+       /* If we sent something, start DMA if necessary */
+       if (total > 0 && !(port->ip_sscr & IOC4_SSCR_DMA_EN)) {
+               port->ip_sscr |= IOC4_SSCR_DMA_EN;
+               writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+       }
+
+       /* Store the new producer pointer.  If tx is disabled, we stuff the
+        * data into the ring buffer, but we don't actually start tx.
+        */
+       if (!uart_tx_stopped(port->ip_port)) {
+               writel(prod_ptr, &port->ip_serial_regs->stpir);
+
+               /* If we are now transmitting, enable tx_mt interrupt so we
+                * can disable DMA if necessary when the tx finishes.
+                */
+               if (total > 0)
+                       enable_intrs(port, hooks->intr_tx_mt);
+       }
+       port->ip_tx_prod = prod_ptr;
+       return total;
+}
+
+/**
+ * disable_intrs - disable interrupts
+ * @port: port to enable
+ * @mask: mask to use
+ */
+static void disable_intrs(struct ioc4_port *port, uint32_t mask)
+{
+       struct hooks *hooks = port->ip_hooks;
+
+       if (port->ip_ienb & mask) {
+               write_ireg(port->ip_ioc4_soft, mask, IOC4_W_IEC,
+                                       IOC4_SIO_INTR_TYPE);
+               port->ip_ienb &= ~mask;
+       }
+
+       if (!port->ip_ienb)
+               write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error,
+                               IOC4_W_IEC, IOC4_OTHER_INTR_TYPE);
+}
+
+/**
+ * set_notification - Modify event notification
+ * @port: port to use
+ * @mask: events mask
+ * @set_on: set ?
+ */
+static int set_notification(struct ioc4_port *port, int mask, int set_on)
+{
+       struct hooks *hooks = port->ip_hooks;
+       uint32_t intrbits, sscrbits;
+
+       BUG_ON(!mask);
+
+       intrbits = sscrbits = 0;
+
+       if (mask & N_DATA_READY)
+               intrbits |= (hooks->intr_rx_timer | hooks->intr_rx_high);
+       if (mask & N_OUTPUT_LOWAT)
+               intrbits |= hooks->intr_tx_explicit;
+       if (mask & N_DDCD) {
+               intrbits |= hooks->intr_delta_dcd;
+               sscrbits |= IOC4_SSCR_RX_RING_DCD;
+       }
+       if (mask & N_DCTS)
+               intrbits |= hooks->intr_delta_cts;
+
+       if (set_on) {
+               enable_intrs(port, intrbits);
+               port->ip_notify |= mask;
+               port->ip_sscr |= sscrbits;
+       } else {
+               disable_intrs(port, intrbits);
+               port->ip_notify &= ~mask;
+               port->ip_sscr &= ~sscrbits;
+       }
+
+       /* We require DMA if either DATA_READY or DDCD notification is
+        * currently requested. If neither of these is requested and
+        * there is currently no tx in progress, DMA may be disabled.
+        */
+       if (port->ip_notify & (N_DATA_READY | N_DDCD))
+               port->ip_sscr |= IOC4_SSCR_DMA_EN;
+       else if (!(port->ip_ienb & hooks->intr_tx_mt))
+               port->ip_sscr &= ~IOC4_SSCR_DMA_EN;
+
+       writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+       return 0;
+}
+
+/**
+ * set_mcr - set the master control reg
+ * @the_port: port to use
+ * @set: set ?
+ * @mask1: mcr mask
+ * @mask2: shadow mask
+ */
+static inline int set_mcr(struct uart_port *the_port, int set,
+               int mask1, int mask2)
+{
+       struct ioc4_port *port = get_ioc4_port(the_port);
+       uint32_t shadow;
+       int spiniter = 0;
+       char mcr;
+
+       if (!port)
+               return -1;
+
+       /* Pause the DMA interface if necessary */
+       if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
+               writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE,
+                       &port->ip_serial_regs->sscr);
+               while ((readl(&port->ip_serial_regs->sscr)
+                                       & IOC4_SSCR_PAUSE_STATE) == 0) {
+                       spiniter++;
+                       if (spiniter > MAXITER)
+                               return -1;
+               }
+       }
+       shadow = readl(&port->ip_serial_regs->shadow);
+       mcr = (shadow & 0xff000000) >> 24;
+
+       /* Set new value */
+       if (set) {
+               mcr |= mask1;
+               shadow |= mask2;
+       } else {
+               mcr &= ~mask1;
+               shadow &= ~mask2;
+       }
+       writeb(mcr, &port->ip_uart_regs->i4u_mcr);
+       writel(shadow, &port->ip_serial_regs->shadow);
+
+       /* Re-enable the DMA interface if necessary */
+       if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
+               writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+       }
+       return 0;
+}
+
+/**
+ * ioc4_set_proto - set the protocol for the port
+ * @port: port to use
+ * @proto: protocol to use
+ */
+static int ioc4_set_proto(struct ioc4_port *port, enum sio_proto proto)
+{
+       struct hooks *hooks = port->ip_hooks;
+
+       switch (proto) {
+       case PROTO_RS232:
+               /* Clear the appropriate GIO pin */
+               writel(0, (&port->ip_mem->gppr_0 +
+                                 hooks->rs422_select_pin));
+               break;
+
+       case PROTO_RS422:
+               /* Set the appropriate GIO pin */
+               writel(1, (&port->ip_mem->gppr_0 +
+                                 hooks->rs422_select_pin));
+               break;
+
+       default:
+               return 1;
+       }
+       return 0;
+}
+
+/**
+ * transmit_chars - upper level write, called with ip_lock
+ * @the_port: port to write
+ */
+static void transmit_chars(struct uart_port *the_port)
+{
+       int xmit_count, tail, head;
+       int result;
+       char *start;
+       struct tty_struct *tty;
+       struct ioc4_port *port = get_ioc4_port(the_port);
+       struct uart_info *info;
+
+       if (!the_port)
+               return;
+       if (!port)
+               return;
+
+       info = the_port->info;
+       tty = info->tty;
+
+       if (uart_circ_empty(&info->xmit) || uart_tx_stopped(the_port)) {
+               /* Nothing to do or hw stopped */
+               set_notification(port, N_ALL_OUTPUT, 0);
+               return;
+       }
+
+       head = info->xmit.head;
+       tail = info->xmit.tail;
+       start = (char *)&info->xmit.buf[tail];
+
+       /* write out all the data or until the end of the buffer */
+       xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail);
+       if (xmit_count > 0) {
+               result = do_write(port, start, xmit_count);
+               if (result > 0) {
+                       /* booking */
+                       xmit_count -= result;
+                       the_port->icount.tx += result;
+                       /* advance the pointers */
+                       tail += result;
+                       tail &= UART_XMIT_SIZE - 1;
+                       info->xmit.tail = tail;
+                       start = (char *)&info->xmit.buf[tail];
+               }
+       }
+       if (uart_circ_chars_pending(&info->xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(the_port);
+
+       if (uart_circ_empty(&info->xmit)) {
+               set_notification(port, N_OUTPUT_LOWAT, 0);
+       } else {
+               set_notification(port, N_OUTPUT_LOWAT, 1);
+       }
+}
+
+/**
+ * ioc4_change_speed - change the speed of the port
+ * @the_port: port to change
+ * @new_termios: new termios settings
+ * @old_termios: old termios settings
+ */
+static void
+ioc4_change_speed(struct uart_port *the_port,
+                 struct termios *new_termios, struct termios *old_termios)
+{
+       struct ioc4_port *port = get_ioc4_port(the_port);
+       int baud, bits;
+       unsigned cflag;
+       int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
+       struct uart_info *info = the_port->info;
+
+       cflag = new_termios->c_cflag;
+
+       switch (cflag & CSIZE) {
+       case CS5:
+               new_data = 5;
+               bits = 7;
+               break;
+       case CS6:
+               new_data = 6;
+               bits = 8;
+               break;
+       case CS7:
+               new_data = 7;
+               bits = 9;
+               break;
+       case CS8:
+               new_data = 8;
+               bits = 10;
+               break;
+       default:
+               /* cuz we always need a default ... */
+               new_data = 5;
+               bits = 7;
+               break;
+       }
+       if (cflag & CSTOPB) {
+               bits++;
+               new_stop = 1;
+       }
+       if (cflag & PARENB) {
+               bits++;
+               new_parity_enable = 1;
+               if (cflag & PARODD)
+                       new_parity = 1;
+       }
+       baud = uart_get_baud_rate(the_port, new_termios, old_termios,
+                               MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED);
+       DPRINT_CONFIG(("%s: returned baud %d\n", __FUNCTION__, baud));
+
+       /* default is 9600 */
+       if (!baud)
+               baud = 9600;
+
+       if (!the_port->fifosize)
+               the_port->fifosize = IOC4_MAX_CHARS;
+       the_port->timeout = ((the_port->fifosize * HZ * bits) / (baud / 10));
+       the_port->timeout += HZ / 50;   /* Add .02 seconds of slop */
+
+       the_port->ignore_status_mask = N_ALL_INPUT;
+
+       if (I_IGNPAR(info->tty))
+               the_port->ignore_status_mask &= ~(N_PARITY_ERROR
+                                               | N_FRAMING_ERROR);
+       if (I_IGNBRK(info->tty)) {
+               the_port->ignore_status_mask &= ~N_BREAK;
+               if (I_IGNPAR(info->tty))
+                       the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;
+       }
+       if (!(cflag & CREAD)) {
+               /* ignore everything */
+               the_port->ignore_status_mask &= ~N_DATA_READY;
+       }
+
+       if (cflag & CRTSCTS) {
+               info->flags |= ASYNC_CTS_FLOW;
+               port->ip_sscr |= IOC4_SSCR_HFC_EN;
+       }
+       else {
+               info->flags &= ~ASYNC_CTS_FLOW;
+               port->ip_sscr &= ~IOC4_SSCR_HFC_EN;
+       }
+       writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+
+       /* Set the configuration and proper notification call */
+       DPRINT_CONFIG(("%s : port 0x%p cflag 0%o "
+               "config_port(baud %d data %d stop %d p enable %d parity %d),"
+               " notification 0x%x\n",
+            __FUNCTION__, (void *)port, cflag, baud, new_data, new_stop,
+            new_parity_enable, new_parity, the_port->ignore_status_mask));
+
+       if ((config_port(port, baud,            /* baud */
+                        new_data,              /* byte size */
+                        new_stop,              /* stop bits */
+                        new_parity_enable,     /* set parity */
+                        new_parity)) >= 0) {   /* parity 1==odd */
+               set_notification(port, the_port->ignore_status_mask, 1);
+       }
+}
+
+/**
+ * ic4_startup_local - Start up the serial port - returns >= 0 if no errors
+ * @the_port: Port to operate on
+ */
+static inline int ic4_startup_local(struct uart_port *the_port)
+{
+       int retval = 0;
+       struct ioc4_port *port;
+       struct uart_info *info;
+
+       if (!the_port)
+               return -1;
+
+       port = get_ioc4_port(the_port);
+       if (!port)
+               return -1;
+
+       info = the_port->info;
+       if (info->flags & UIF_INITIALIZED) {
+               return retval;
+       }
+
+       if (info->tty) {
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+               clear_bit(TTY_IO_ERROR, &info->tty->flags);
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                       info->tty->alt_speed = 57600;
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                       info->tty->alt_speed = 115200;
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                       info->tty->alt_speed = 230400;
+               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                       info->tty->alt_speed = 460800;
+       }
+       local_open(port);
+
+       /* set the speed of the serial port */
+       ioc4_change_speed(the_port, info->tty->termios, (struct termios *)0);
+
+       info->flags |= UIF_INITIALIZED;
+       return 0;
+}
+
+/*
+ * ioc4_cb_output_lowat - called when the output low water mark is hit
+ * @port: port to output
+ */
+static void ioc4_cb_output_lowat(struct ioc4_port *port)
+{
+       /* ip_lock is set on the call here */
+       if (port->ip_port) {
+               transmit_chars(port->ip_port);
+       }
+}
+
+/**
+ * handle_intr - service any interrupts for the given port - 2nd level
+ *                     called via sd_intr
+ * @arg: handler arg
+ * @sio_ir: ioc4regs
+ */
+static void handle_intr(void *arg, uint32_t sio_ir)
+{
+       struct ioc4_port *port = (struct ioc4_port *)arg;
+       struct hooks *hooks = port->ip_hooks;
+       unsigned int rx_high_rd_aborted = 0;
+       unsigned int flags;
+       struct uart_port *the_port;
+       int loop_counter;
+
+       /* Possible race condition here: The tx_mt interrupt bit may be
+        * cleared without the intervention of the interrupt handler,
+        * e.g. by a write.  If the top level interrupt handler reads a
+        * tx_mt, then some other processor does a write, starting up
+        * output, then we come in here, see the tx_mt and stop DMA, the
+        * output started by the other processor will hang.  Thus we can
+        * only rely on tx_mt being legitimate if it is read while the
+        * port lock is held.  Therefore this bit must be ignored in the
+        * passed in interrupt mask which was read by the top level
+        * interrupt handler since the port lock was not held at the time
+        * it was read.  We can only rely on this bit being accurate if it
+        * is read while the port lock is held.  So we'll clear it for now,
+        * and reload it later once we have the port lock.
+        */
+       sio_ir &= ~(hooks->intr_tx_mt);
+
+       spin_lock_irqsave(&port->ip_lock, flags);
+
+       loop_counter = MAXITER; /* to avoid hangs */
+
+       do {
+               uint32_t shadow;
+
+               if ( loop_counter-- <= 0 ) {
+                       printk(KERN_WARNING "IOC4 serial: "
+                                       "possible hang condition/"
+                                       "port stuck on interrupt.\n");
+                       break;
+               }
+
+               /* Handle a DCD change */
+               if (sio_ir & hooks->intr_delta_dcd) {
+                       /* ACK the interrupt */
+                       writel(hooks->intr_delta_dcd,
+                               &port->ip_mem->sio_ir);
+
+                       shadow = readl(&port->ip_serial_regs->shadow);
+
+                       if ((port->ip_notify & N_DDCD)
+                                       && (shadow & IOC4_SHADOW_DCD)
+                                       && (port->ip_port)) {
+                               the_port = port->ip_port;
+                               the_port->icount.dcd = 1;
+                               wake_up_interruptible
+                                           (&the_port-> info->delta_msr_wait);
+                       } else if ((port->ip_notify & N_DDCD)
+                                       && !(shadow & IOC4_SHADOW_DCD)) {
+                               /* Flag delta DCD/no DCD */
+                               port->ip_flags |= DCD_ON;
+                       }
+               }
+
+               /* Handle a CTS change */
+               if (sio_ir & hooks->intr_delta_cts) {
+                       /* ACK the interrupt */
+                       writel(hooks->intr_delta_cts,
+                                       &port->ip_mem->sio_ir);
+
+                       shadow = readl(&port->ip_serial_regs->shadow);
+
+                       if ((port->ip_notify & N_DCTS)
+                                       && (port->ip_port)) {
+                               the_port = port->ip_port;
+                               the_port->icount.cts =
+                                       (shadow & IOC4_SHADOW_CTS) ? 1 : 0;
+                               wake_up_interruptible
+                                       (&the_port->info->delta_msr_wait);
+                       }
+               }
+
+               /* rx timeout interrupt.  Must be some data available.  Put this
+                * before the check for rx_high since servicing this condition
+                * may cause that condition to clear.
+                */
+               if (sio_ir & hooks->intr_rx_timer) {
+                       /* ACK the interrupt */
+                       writel(hooks->intr_rx_timer,
+                               &port->ip_mem->sio_ir);
+
+                       if ((port->ip_notify & N_DATA_READY)
+                                       && (port->ip_port)) {
+                               /* ip_lock is set on call here */
+                               receive_chars(port->ip_port);
+                       }
+               }
+
+               /* rx high interrupt. Must be after rx_timer.  */
+               else if (sio_ir & hooks->intr_rx_high) {
+                       /* Data available, notify upper layer */
+                       if ((port->ip_notify & N_DATA_READY)
+                                               && port->ip_port) {
+                               /* ip_lock is set on call here */
+                               receive_chars(port->ip_port);
+                       }
+
+                       /* We can't ACK this interrupt.  If receive_chars didn't
+                        * cause the condition to clear, we'll have to disable
+                        * the interrupt until the data is drained.
+                        * If the read was aborted, don't disable the interrupt
+                        * as this may cause us to hang indefinitely.  An
+                        * aborted read generally means that this interrupt
+                        * hasn't been delivered to the cpu yet anyway, even
+                        * though we see it as asserted when we read the sio_ir.
+                        */
+                       if ((sio_ir = PENDING(port)) & hooks->intr_rx_high) {
+                               if ((port->ip_flags & READ_ABORTED) == 0) {
+                                       port->ip_ienb &= ~hooks->intr_rx_high;
+                                       port->ip_flags |= INPUT_HIGH;
+                               } else {
+                                       rx_high_rd_aborted++;
+                               }
+                       }
+               }
+
+               /* We got a low water interrupt: notify upper layer to
+                * send more data.  Must come before tx_mt since servicing
+                * this condition may cause that condition to clear.
+                */
+               if (sio_ir & hooks->intr_tx_explicit) {
+                       port->ip_flags &= ~LOWAT_WRITTEN;
+
+                       /* ACK the interrupt */
+                       writel(hooks->intr_tx_explicit,
+                                       &port->ip_mem->sio_ir);
+
+                       if (port->ip_notify & N_OUTPUT_LOWAT)
+                               ioc4_cb_output_lowat(port);
+               }
+
+               /* Handle tx_mt.  Must come after tx_explicit.  */
+               else if (sio_ir & hooks->intr_tx_mt) {
+                       /* If we are expecting a lowat notification
+                        * and we get to this point it probably means that for
+                        * some reason the tx_explicit didn't work as expected
+                        * (that can legitimately happen if the output buffer is
+                        * filled up in just the right way).
+                        * So send the notification now.
+                        */
+                       if (port->ip_notify & N_OUTPUT_LOWAT) {
+                               ioc4_cb_output_lowat(port);
+
+                               /* We need to reload the sio_ir since the lowat
+                                * call may have caused another write to occur,
+                                * clearing the tx_mt condition.
+                                */
+                               sio_ir = PENDING(port);
+                       }
+
+                       /* If the tx_mt condition still persists even after the
+                        * lowat call, we've got some work to do.
+                        */
+                       if (sio_ir & hooks->intr_tx_mt) {
+
+                               /* If we are not currently expecting DMA input,
+                                * and the transmitter has just gone idle,
+                                * there is no longer any reason for DMA, so
+                                * disable it.
+                                */
+                               if (!(port->ip_notify
+                                               & (N_DATA_READY | N_DDCD))) {
+                                       BUG_ON(!(port->ip_sscr
+                                                       & IOC4_SSCR_DMA_EN));
+                                       port->ip_sscr &= ~IOC4_SSCR_DMA_EN;
+                                       writel(port->ip_sscr,
+                                          &port->ip_serial_regs->sscr);
+                               }
+
+                               /* Prevent infinite tx_mt interrupt */
+                               port->ip_ienb &= ~hooks->intr_tx_mt;
+                       }
+               }
+               sio_ir = PENDING(port);
+
+               /* if the read was aborted and only hooks->intr_rx_high,
+                * clear hooks->intr_rx_high, so we do not loop forever.
+                */
+
+               if (rx_high_rd_aborted && (sio_ir == hooks->intr_rx_high)) {
+                       sio_ir &= ~hooks->intr_rx_high;
+               }
+       } while (sio_ir & hooks->intr_all);
+
+       spin_unlock_irqrestore(&port->ip_lock, flags);
+
+       /* Re-enable interrupts before returning from interrupt handler.
+        * Getting interrupted here is okay.  It'll just v() our semaphore, and
+        * we'll come through the loop again.
+        */
+
+       write_ireg(port->ip_ioc4_soft, port->ip_ienb, IOC4_W_IES,
+                                                       IOC4_SIO_INTR_TYPE);
+}
+
+/*
+ * ioc4_cb_post_ncs - called for some basic errors
+ * @port: port to use
+ * @ncs: event
+ */
+static void ioc4_cb_post_ncs(struct uart_port *the_port, int ncs)
+{
+       struct uart_icount *icount;
+
+       icount = &the_port->icount;
+
+       if (ncs & NCS_BREAK)
+               icount->brk++;
+       if (ncs & NCS_FRAMING)
+               icount->frame++;
+       if (ncs & NCS_OVERRUN)
+               icount->overrun++;
+       if (ncs & NCS_PARITY)
+               icount->parity++;
+}
+
+/**
+ * do_read - Read in bytes from the port.  Return the number of bytes
+ *                     actually read.
+ * @the_port: port to use
+ * @buf: place to put the stuff we read
+ * @len: how big 'buf' is
+ */
+
+static inline int do_read(struct uart_port *the_port, unsigned char *buf,
+                               int len)
+{
+       int prod_ptr, cons_ptr, total;
+       struct ioc4_port *port = get_ioc4_port(the_port);
+       struct ring *inring;
+       struct ring_entry *entry;
+       struct hooks *hooks = port->ip_hooks;
+       int byte_num;
+       char *sc;
+       int loop_counter;
+
+       BUG_ON(!(len >= 0));
+       BUG_ON(!port);
+
+       /* There is a nasty timing issue in the IOC4. When the rx_timer
+        * expires or the rx_high condition arises, we take an interrupt.
+        * At some point while servicing the interrupt, we read bytes from
+        * the ring buffer and re-arm the rx_timer.  However the rx_timer is
+        * not started until the first byte is received *after* it is armed,
+        * and any bytes pending in the rx construction buffers are not drained
+        * to memory until either there are 4 bytes available or the rx_timer
+        * expires.  This leads to a potential situation where data is left
+        * in the construction buffers forever - 1 to 3 bytes were received
+        * after the interrupt was generated but before the rx_timer was
+        * re-armed. At that point as long as no subsequent bytes are received
+        * the timer will never be started and the bytes will remain in the
+        * construction buffer forever.  The solution is to execute a DRAIN
+        * command after rearming the timer.  This way any bytes received before
+        * the DRAIN will be drained to memory, and any bytes received after
+        * the DRAIN will start the TIMER and be drained when it expires.
+        * Luckily, this only needs to be done when the DMA buffer is empty
+        * since there is no requirement that this function return all
+        * available data as long as it returns some.
+        */
+       /* Re-arm the timer */
+       writel(port->ip_rx_cons | IOC4_SRCIR_ARM,
+                       &port->ip_serial_regs->srcir);
+
+       prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
+       cons_ptr = port->ip_rx_cons;
+
+       if (prod_ptr == cons_ptr) {
+               int reset_dma = 0;
+
+               /* Input buffer appears empty, do a flush. */
+
+               /* DMA must be enabled for this to work. */
+               if (!(port->ip_sscr & IOC4_SSCR_DMA_EN)) {
+                       port->ip_sscr |= IOC4_SSCR_DMA_EN;
+                       reset_dma = 1;
+               }
+
+               /* Potential race condition: we must reload the srpir after
+                * issuing the drain command, otherwise we could think the rx
+                * buffer is empty, then take a very long interrupt, and when
+                * we come back it's full and we wait forever for the drain to
+                * complete.
+                */
+               writel(port->ip_sscr | IOC4_SSCR_RX_DRAIN,
+                               &port->ip_serial_regs->sscr);
+               prod_ptr = readl(&port->ip_serial_regs->srpir)
+                               & PROD_CONS_MASK;
+
+               /* We must not wait for the DRAIN to complete unless there are
+                * at least 8 bytes (2 ring entries) available to receive the
+                * data otherwise the DRAIN will never complete and we'll
+                * deadlock here.
+                * In fact, to make things easier, I'll just ignore the flush if
+                * there is any data at all now available.
+                */
+               if (prod_ptr == cons_ptr) {
+                       loop_counter = 0;
+                       while (readl(&port->ip_serial_regs->sscr) &
+                                               IOC4_SSCR_RX_DRAIN) {
+                               loop_counter++;
+                               if (loop_counter > MAXITER)
+                                       return -1;
+                       }
+
+                       /* SIGH. We have to reload the prod_ptr *again* since
+                        * the drain may have caused it to change
+                        */
+                       prod_ptr = readl(&port->ip_serial_regs->srpir)
+                                                       & PROD_CONS_MASK;
+               }
+               if (reset_dma) {
+                       port->ip_sscr &= ~IOC4_SSCR_DMA_EN;
+                       writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+               }
+       }
+       inring = port->ip_inring;
+       port->ip_flags &= ~READ_ABORTED;
+
+       total = 0;
+       loop_counter = 0xfffff; /* to avoid hangs */
+
+       /* Grab bytes from the hardware */
+       while ((prod_ptr != cons_ptr) && (len > 0)) {
+               entry = (struct ring_entry *)((caddr_t)inring + cons_ptr);
+
+               if ( loop_counter-- <= 0 ) {
+                       printk(KERN_WARNING "IOC4 serial: "
+                                       "possible hang condition/"
+                                       "port stuck on read.\n");
+                       break;
+               }
+
+               /* According to the producer pointer, this ring entry
+                * must contain some data.  But if the PIO happened faster
+                * than the DMA, the data may not be available yet, so let's
+                * wait until it arrives.
+                */
+               if ((entry->ring_allsc & RING_ANY_VALID) == 0) {
+                       /* Indicate the read is aborted so we don't disable
+                        * the interrupt thinking that the consumer is
+                        * congested.
+                        */
+                       port->ip_flags |= READ_ABORTED;
+                       len = 0;
+                       break;
+               }
+
+               /* Load the bytes/status out of the ring entry */
+               for (byte_num = 0; byte_num < 4 && len > 0; byte_num++) {
+                       sc = &(entry->ring_sc[byte_num]);
+
+                       /* Check for change in modem state or overrun */
+                       if ((*sc & IOC4_RXSB_MODEM_VALID)
+                                               && (port->ip_notify & N_DDCD)) {
+                               /* Notify upper layer if DCD dropped */
+
+                               if ((port->ip_flags & DCD_ON)
+                                               && !(*sc & IOC4_RXSB_DCD)) {
+
+                                       /* If we have already copied some data,
+                                        * return it.  We'll pick up the carrier
+                                        * drop on the next pass.  That way we
+                                        * don't throw away the data that has
+                                        * already been copied back to
+                                        * the caller's buffer.
+                                        */
+                                       if (total > 0) {
+                                               len = 0;
+                                               break;
+                                       }
+                                       port->ip_flags &= ~DCD_ON;
+
+                                       /* Turn off this notification so the
+                                        * carrier drop protocol won't see it
+                                        * again when it does a read.
+                                        */
+                                       *sc &= ~IOC4_RXSB_MODEM_VALID;
+
+                                       /* To keep things consistent, we need
+                                        * to update the consumer pointer so
+                                        * the next reader won't come in and
+                                        * try to read the same ring entries
+                                        * again. This must be done here before
+                                        * the dcd change.
+                                        */
+
+                                       if ((entry->ring_allsc & RING_ANY_VALID)
+                                                                       == 0) {
+                                               cons_ptr += (int)sizeof
+                                                       (struct ring_entry);
+                                               cons_ptr &= PROD_CONS_MASK;
+                                       }
+                                       writel(cons_ptr,
+                                               &port->ip_serial_regs->srcir);
+                                       port->ip_rx_cons = cons_ptr;
+
+                                       /* Notify upper layer of carrier drop */
+                                       if ((port->ip_notify & N_DDCD)
+                                                  && port->ip_port) {
+                                               the_port->icount.dcd = 0;
+                                               wake_up_interruptible
+                                                   (&the_port->info->
+                                                       delta_msr_wait);
+                                       }
+
+                                       /* If we had any data to return, we
+                                        * would have returned it above.
+                                        */
+                                       return 0;
+                               }
+                       }
+                       if (*sc & IOC4_RXSB_MODEM_VALID) {
+                               /* Notify that an input overrun occurred */
+                               if ((*sc & IOC4_RXSB_OVERRUN)
+                                   && (port->ip_notify & N_OVERRUN_ERROR)) {
+                                       ioc4_cb_post_ncs(the_port, NCS_OVERRUN);
+                               }
+                               /* Don't look at this byte again */
+                               *sc &= ~IOC4_RXSB_MODEM_VALID;
+                       }
+
+                       /* Check for valid data or RX errors */
+                       if ((*sc & IOC4_RXSB_DATA_VALID) &&
+                                       ((*sc & (IOC4_RXSB_PAR_ERR
+                                                       | IOC4_RXSB_FRAME_ERR
+                                                       | IOC4_RXSB_BREAK))
+                                       && (port->ip_notify & (N_PARITY_ERROR
+                                                       | N_FRAMING_ERROR
+                                                       | N_BREAK)))) {
+                               /* There is an error condition on the next byte.
+                                * If we have already transferred some bytes,
+                                * we'll stop here. Otherwise if this is the
+                                * first byte to be read, we'll just transfer
+                                * it alone after notifying the
+                                * upper layer of its status.
+                                */
+                               if (total > 0) {
+                                       len = 0;
+                                       break;
+                               } else {
+                                       if ((*sc & IOC4_RXSB_PAR_ERR) &&
+                                          (port->ip_notify & N_PARITY_ERROR)) {
+                                               ioc4_cb_post_ncs(the_port,
+                                                               NCS_PARITY);
+                                       }
+                                       if ((*sc & IOC4_RXSB_FRAME_ERR) &&
+                                          (port->ip_notify & N_FRAMING_ERROR)){
+                                               ioc4_cb_post_ncs(the_port,
+                                                               NCS_FRAMING);
+                                       }
+                                       if ((*sc & IOC4_RXSB_BREAK)
+                                           && (port->ip_notify & N_BREAK)) {
+                                                       ioc4_cb_post_ncs
+                                                                   (the_port,
+                                                                    NCS_BREAK);
+                                       }
+                                       len = 1;
+                               }
+                       }
+                       if (*sc & IOC4_RXSB_DATA_VALID) {
+                               *sc &= ~IOC4_RXSB_DATA_VALID;
+                               *buf = entry->ring_data[byte_num];
+                               buf++;
+                               len--;
+                               total++;
+                       }
+               }
+
+               /* If we used up this entry entirely, go on to the next one,
+                * otherwise we must have run out of buffer space, so
+                * leave the consumer pointer here for the next read in case
+                * there are still unread bytes in this entry.
+                */
+               if ((entry->ring_allsc & RING_ANY_VALID) == 0) {
+                       cons_ptr += (int)sizeof(struct ring_entry);
+                       cons_ptr &= PROD_CONS_MASK;
+               }
+       }
+
+       /* Update consumer pointer and re-arm rx timer interrupt */
+       writel(cons_ptr, &port->ip_serial_regs->srcir);
+       port->ip_rx_cons = cons_ptr;
+
+       /* If we have now dipped below the rx high water mark and we have
+        * rx_high interrupt turned off, we can now turn it back on again.
+        */
+       if ((port->ip_flags & INPUT_HIGH) && (((prod_ptr - cons_ptr)
+                       & PROD_CONS_MASK) < ((port->ip_sscr &
+                               IOC4_SSCR_RX_THRESHOLD)
+                                       << IOC4_PROD_CONS_PTR_OFF))) {
+               port->ip_flags &= ~INPUT_HIGH;
+               enable_intrs(port, hooks->intr_rx_high);
+       }
+       return total;
+}
+/**
+ * receive_chars - upper level read. Called with ip_lock.
+ * @the_port: port to read from
+ */
+static void receive_chars(struct uart_port *the_port)
+{
+       struct tty_struct *tty;
+       unsigned char ch[IOC4_MAX_CHARS];
+       int read_count, request_count;
+       struct uart_icount *icount;
+       struct uart_info *info = the_port->info;
+
+       /* Make sure all the pointers are "good" ones */
+       if (!info)
+               return;
+       if (!info->tty)
+               return;
+
+       tty = info->tty;
+
+       request_count = TTY_FLIPBUF_SIZE - tty->flip.count - 1;
+
+       if (request_count > 0) {
+               if (request_count > IOC4_MAX_CHARS - 2)
+                       request_count = IOC4_MAX_CHARS - 2;
+               icount = &the_port->icount;
+               read_count = do_read(the_port, ch, request_count);
+               if (read_count > 0) {
+                       memcpy(tty->flip.char_buf_ptr, ch, read_count);
+                       memset(tty->flip.flag_buf_ptr, TTY_NORMAL, read_count);
+                       tty->flip.char_buf_ptr += read_count;
+                       tty->flip.flag_buf_ptr += read_count;
+                       tty->flip.count += read_count;
+                       icount->rx += read_count;
+               }
+       }
+       tty_flip_buffer_push(tty);
+}
+
+/**
+ * ic4_type - What type of console are we?
+ * @port: Port to operate with (we ignore since we only have one port)
+ *
+ */
+static const char *ic4_type(struct uart_port *the_port)
+{
+       return "SGI IOC4 Serial";
+}
+
+/**
+ * ic4_tx_empty - Is the transmitter empty?  We pretend we're always empty
+ * @port: Port to operate on (we ignore since we always return 1)
+ *
+ */
+static unsigned int ic4_tx_empty(struct uart_port *the_port)
+{
+       return 1;
+}
+
+/**
+ * ic4_stop_tx - stop the transmitter
+ * @port: Port to operate on
+ * @tty_stop: Set to 1 if called via uart_stop
+ *
+ */
+static void ic4_stop_tx(struct uart_port *the_port, unsigned int tty_stop)
+{
+}
+
+/**
+ * null_void_function -
+ * @port: Port to operate on
+ *
+ */
+static void null_void_function(struct uart_port *the_port)
+{
+}
+
+/**
+ * ic4_shutdown - shut down the port - free irq and disable
+ * @port: Port to shut down
+ *
+ */
+static void ic4_shutdown(struct uart_port *the_port)
+{
+       unsigned long port_flags;
+       struct ioc4_port *port;
+       struct uart_info *info;
+
+       port = get_ioc4_port(the_port);
+       if (!port)
+               return;
+
+       info = the_port->info;
+
+       if (!(info->flags & UIF_INITIALIZED))
+               return;
+
+       wake_up_interruptible(&info->delta_msr_wait);
+
+       if (info->tty)
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       spin_lock_irqsave(&port->ip_lock, port_flags);
+       set_notification(port, N_ALL, 0);
+       info->flags &= ~UIF_INITIALIZED;
+       spin_unlock_irqrestore(&port->ip_lock, port_flags);
+}
+
+/**
+ * ic4_set_mctrl - set control lines (dtr, rts, etc)
+ * @port: Port to operate on
+ * @mctrl: Lines to set/unset
+ *
+ */
+static void ic4_set_mctrl(struct uart_port *the_port, unsigned int mctrl)
+{
+       unsigned char mcr = 0;
+
+       if (mctrl & TIOCM_RTS)
+               mcr |= UART_MCR_RTS;
+       if (mctrl & TIOCM_DTR)
+               mcr |= UART_MCR_DTR;
+       if (mctrl & TIOCM_OUT1)
+               mcr |= UART_MCR_OUT1;
+       if (mctrl & TIOCM_OUT2)
+               mcr |= UART_MCR_OUT2;
+       if (mctrl & TIOCM_LOOP)
+               mcr |= UART_MCR_LOOP;
+
+       set_mcr(the_port, 1, mcr, IOC4_SHADOW_DTR);
+}
+
+/**
+ * ic4_get_mctrl - get control line info
+ * @port: port to operate on
+ *
+ */
+static unsigned int ic4_get_mctrl(struct uart_port *the_port)
+{
+       struct ioc4_port *port = get_ioc4_port(the_port);
+       uint32_t shadow;
+       unsigned int ret = 0;
+
+       if (!port)
+               return 0;
+
+       shadow = readl(&port->ip_serial_regs->shadow);
+       if (shadow & IOC4_SHADOW_DCD)
+               ret |= TIOCM_CAR;
+       if (shadow & IOC4_SHADOW_DR)
+               ret |= TIOCM_DSR;
+       if (shadow & IOC4_SHADOW_CTS)
+               ret |= TIOCM_CTS;
+       return ret;
+}
+
+/**
+ * ic4_start_tx - Start transmitter, flush any output
+ * @port: Port to operate on
+ * @tty_stop: Set to 1 if called via uart_start
+ *
+ */
+static void ic4_start_tx(struct uart_port *the_port, unsigned int tty_stop)
+{
+       struct ioc4_port *port = get_ioc4_port(the_port);
+       unsigned long flags;
+
+       if (port) {
+               spin_lock_irqsave(&port->ip_lock, flags);
+               transmit_chars(the_port);
+               spin_unlock_irqrestore(&port->ip_lock, flags);
+       }
+}
+
+/**
+ * ic4_break_ctl - handle breaks
+ * @port: Port to operate on
+ * @break_state: Break state
+ *
+ */
+static void ic4_break_ctl(struct uart_port *the_port, int break_state)
+{
+}
+
+/**
+ * ic4_startup - Start up the serial port - always return 0 (We're always on)
+ * @port: Port to operate on
+ *
+ */
+static int ic4_startup(struct uart_port *the_port)
+{
+       int retval;
+       struct ioc4_port *port;
+       struct ioc4_control *control;
+       struct uart_info *info;
+       unsigned long port_flags;
+
+       if (!the_port) {
+               return -ENODEV;
+       }
+       port = get_ioc4_port(the_port);
+       if (!port) {
+               return -ENODEV;
+       }
+       info = the_port->info;
+
+       control = port->ip_control;
+       if (!control) {
+               return -ENODEV;
+       }
+
+       /* Start up the serial port */
+       spin_lock_irqsave(&port->ip_lock, port_flags);
+       retval = ic4_startup_local(the_port);
+       spin_unlock_irqrestore(&port->ip_lock, port_flags);
+       return retval;
+}
+
+/**
+ * ic4_set_termios - set termios stuff
+ * @port: port to operate on
+ * @termios: New settings
+ * @termios: Old
+ *
+ */
+static void
+ic4_set_termios(struct uart_port *the_port,
+               struct termios *termios, struct termios *old_termios)
+{
+       struct ioc4_port *port = get_ioc4_port(the_port);
+       unsigned long port_flags;
+
+       spin_lock_irqsave(&port->ip_lock, port_flags);
+       ioc4_change_speed(the_port, termios, old_termios);
+       spin_unlock_irqrestore(&port->ip_lock, port_flags);
+}
+
+/**
+ * ic4_request_port - allocate resources for port - no op....
+ * @port: port to operate on
+ *
+ */
+static int ic4_request_port(struct uart_port *port)
+{
+       return 0;
+}
+
+/* Associate the uart functions above - given to serial core */
+
+static struct uart_ops ioc4_ops = {
+       .tx_empty       = ic4_tx_empty,
+       .set_mctrl      = ic4_set_mctrl,
+       .get_mctrl      = ic4_get_mctrl,
+       .stop_tx        = ic4_stop_tx,
+       .start_tx       = ic4_start_tx,
+       .stop_rx        = null_void_function,
+       .enable_ms      = null_void_function,
+       .break_ctl      = ic4_break_ctl,
+       .startup        = ic4_startup,
+       .shutdown       = ic4_shutdown,
+       .set_termios    = ic4_set_termios,
+       .type           = ic4_type,
+       .release_port   = null_void_function,
+       .request_port   = ic4_request_port,
+};
+
+/*
+ * Boot-time initialization code
+ */
+
+static struct uart_driver ioc4_uart = {
+       .owner          = THIS_MODULE,
+       .driver_name    = "ioc4_serial",
+       .dev_name       = DEVICE_NAME,
+       .major          = DEVICE_MAJOR,
+       .minor          = DEVICE_MINOR,
+       .nr             = IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS,
+};
+
+/**
+ * ioc4_serial_core_attach - register with serial core
+ *             This is done during pci probing
+ * @pdev: handle for this card
+ */
+static inline int
+ioc4_serial_core_attach(struct pci_dev *pdev)
+{
+       struct ioc4_port *port;
+       struct uart_port *the_port;
+       struct ioc4_control *control = pci_get_drvdata(pdev);
+       int ii;
+
+       DPRINT_CONFIG(("%s: attach pdev 0x%p - control 0x%p\n",
+                       __FUNCTION__, pdev, (void *)control));
+
+       if (!control)
+               return -ENODEV;
+
+       /* once around for each port on this card */
+       for (ii = 0; ii < IOC4_NUM_SERIAL_PORTS; ii++) {
+               the_port = &control->ic_port[ii].icp_uart_port;
+               port = control->ic_port[ii].icp_port;
+               port->ip_port = the_port;
+
+               DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p\n",
+                               __FUNCTION__, (void *)the_port,
+                               (void *)port));
+
+               spin_lock_init(&the_port->lock);
+               /* membase, iobase and mapbase just need to be non-0 */
+               the_port->membase = (unsigned char __iomem *)1;
+               the_port->line = the_port->iobase = ii;
+               the_port->mapbase = 1;
+               the_port->type = PORT_16550A;
+               the_port->fifosize = IOC4_MAX_CHARS;
+               the_port->ops = &ioc4_ops;
+               the_port->irq = control->ic_irq;
+               the_port->dev = &pdev->dev;
+               if (uart_add_one_port(&ioc4_uart, the_port) < 0) {
+                       printk(KERN_WARNING
+                                      "%s: unable to add port %d\n",
+                                      __FUNCTION__, the_port->line);
+               } else {
+                       DPRINT_CONFIG(
+                                   ("IOC4 serial driver port %d irq = %d\n",
+                                      the_port->line, the_port->irq));
+               }
+               /* all ports are rs232 for now */
+               ioc4_set_proto(port, PROTO_RS232);
+       }
+       return 0;
+}
+
+/**
+ * ioc4_serial_attach_one - register attach function
+ *             called per card found from ioc4_serial_detect as part
+ *             of module_init().
+ * @pdev: handle for this card
+ * @pci_id: pci id for this card
+ */
+int
+ioc4_serial_attach_one(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+{
+       struct ioc4_mem __iomem *mem;
+       unsigned long tmp_addr, tmp_addr1;
+       struct ioc4_serial __iomem *serial;
+       struct ioc4_soft *soft;
+       struct ioc4_control *control;
+       int tmp, ret = 0;
+
+
+       DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __FUNCTION__, pdev, pci_id));
+
+       /* Map in the ioc4 memory */
+       tmp_addr = pci_resource_start(pdev, 0);
+       if (!tmp_addr) {
+               printk(KERN_WARNING
+                        "ioc4 (%p) : unable to get PIO mapping for "
+                               "MEM space\n", (void *)pdev);
+               return -ENODEV;
+       }
+       if (!request_region(tmp_addr, sizeof(struct ioc4_mem), "sioc4_mem")) {
+               printk(KERN_ALERT
+                       "ioc4 (%p): unable to get request region for "
+                       "MEM space\n", (void *)pdev);
+               return -ENODEV;
+       }
+       mem = ioremap(tmp_addr, sizeof(struct ioc4_mem));
+       if (!mem) {
+               printk(KERN_WARNING
+                        "ioc4 (%p) : unable to remap ioc4 memory\n",
+                               (void *)pdev);
+               ret = -ENODEV;
+               goto out1;
+       }
+
+       /* request serial registers */
+       tmp_addr1 = pci_resource_start(pdev, 0) + IOC4_SERIAL_OFFSET;
+
+       if (!request_region(tmp_addr1, sizeof(struct ioc4_serial),
+                                       "sioc4_uart")) {
+               printk(KERN_WARNING
+                       "ioc4 (%p): unable to get request region for "
+                               "uart space\n", (void *)pdev);
+               ret = -ENODEV;
+               goto out1;
+       }
+       serial = ioremap(tmp_addr1, sizeof(struct ioc4_serial));
+       if (!serial) {
+               printk(KERN_WARNING
+                        "ioc4 (%p) : unable to remap ioc4 serial register\n",
+                               (void *)pdev);
+               ret = -ENODEV;
+               goto out2;
+       }
+       DPRINT_CONFIG(("%s : mem 0x%p, serial 0x%p\n",
+                               __FUNCTION__, (void *)mem, (void *)serial));
+
+       /* Get memory for the new card */
+       control = kmalloc(sizeof(struct ioc4_control) * IOC4_NUM_SERIAL_PORTS,
+                                               GFP_KERNEL);
+
+       if (!control) {
+               printk(KERN_WARNING "ioc4_attach_one"
+                      ": unable to get memory for the IOC4\n");
+               ret = -ENOMEM;
+               goto out2;
+       }
+       memset(control, 0, sizeof(struct ioc4_control));
+       pci_set_drvdata(pdev, control);
+
+       /* Allocate the soft structure */
+       soft = kmalloc(sizeof(struct ioc4_soft), GFP_KERNEL);
+       if (!soft) {
+               printk(KERN_WARNING
+                      "ioc4 (%p): unable to get memory for the soft struct\n",
+                      (void *)pdev);
+               ret = -ENOMEM;
+               goto out3;
+       }
+       memset(soft, 0, sizeof(struct ioc4_soft));
+
+       spin_lock_init(&soft->is_ir_lock);
+       soft->is_ioc4_mem_addr = mem;
+       soft->is_ioc4_serial_addr = serial;
+
+       /* Init the IOC4 */
+       pci_read_config_dword(pdev, PCI_COMMAND, &tmp);
+       pci_write_config_dword(pdev, PCI_COMMAND,
+                              tmp | PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
+
+       writel(0xf << IOC4_SIO_CR_CMD_PULSE_SHIFT, &mem->sio_cr);
+
+       /* Enable serial port mode select generic PIO pins as outputs */
+       writel(IOC4_GPCR_UART0_MODESEL | IOC4_GPCR_UART1_MODESEL
+               | IOC4_GPCR_UART2_MODESEL | IOC4_GPCR_UART3_MODESEL,
+               &mem->gpcr_s);
+
+       /* Clear and disable all interrupts */
+       write_ireg(soft, ~0, IOC4_W_IEC, IOC4_SIO_INTR_TYPE);
+       writel(~0, &mem->sio_ir);
+       write_ireg(soft, ~(IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR),
+                       IOC4_W_IEC, IOC4_OTHER_INTR_TYPE);
+       writel(~(IOC4_OTHER_IR_ATA_MEMERR | IOC4_OTHER_IR_ATA_MEMERR),
+                                       &mem->other_ir);
+       control->ic_soft = soft;
+       if (!request_irq(pdev->irq, ioc4_intr, SA_SHIRQ,
+                               "sgi-ioc4serial", (void *)soft)) {
+               control->ic_irq = pdev->irq;
+       } else {
+               printk(KERN_WARNING
+                   "%s : request_irq fails for IRQ 0x%x\n ",
+                       __FUNCTION__, pdev->irq);
+       }
+       if ((ret = ioc4_attach_local(pdev, control, soft,
+                               soft->is_ioc4_mem_addr,
+                               soft->is_ioc4_serial_addr)))
+               goto out4;
+
+       /* register port with the serial core */
+
+       if ((ret = ioc4_serial_core_attach(pdev)))
+               goto out4;
+
+       return ret;
+
+       /* error exits that give back resources */
+out4:
+       kfree(soft);
+out3:
+       kfree(control);
+out2:
+       release_region(tmp_addr1, sizeof(struct ioc4_serial));
+out1:
+       release_region(tmp_addr, sizeof(struct ioc4_mem));
+
+       return ret;
+}
+
+
+/**
+ * ioc4_serial_remove_one - detach function
+ *
+ * @pdev: handle for this card
+ */
+
+#if 0
+void ioc4_serial_remove_one(struct pci_dev *pdev)
+{
+       int ii;
+       struct ioc4_control *control;
+       struct uart_port *the_port;
+       struct ioc4_port *port;
+       struct ioc4_soft *soft;
+
+       control = pci_get_drvdata(pdev);
+
+       for (ii = 0; ii < IOC4_NUM_SERIAL_PORTS; ii++) {
+               the_port = &control->ic_port[ii].icp_uart_port;
+               if (the_port) {
+                       uart_remove_one_port(&ioc4_uart, the_port);
+               }
+               port = control->ic_port[ii].icp_port;
+               if (!(ii & 1) && port) {
+                       pci_free_consistent(port->ip_pdev,
+                                       TOTAL_RING_BUF_SIZE,
+                                       (void *)port->ip_cpu_ringbuf,
+                                       port->ip_dma_ringbuf);
+                       kfree(port);
+               }
+       }
+       soft = control->ic_soft;
+       if (soft) {
+               free_irq(control->ic_irq, (void *)soft);
+               if (soft->is_ioc4_serial_addr) {
+                       release_region((unsigned long)
+                            soft->is_ioc4_serial_addr,
+                               sizeof(struct ioc4_serial));
+               }
+               kfree(soft);
+       }
+       kfree(control);
+       pci_set_drvdata(pdev, NULL);
+       uart_unregister_driver(&ioc4_uart);
+}
+#endif
+
+/**
+ * ioc4_serial_init - module init
+ */
+int ioc4_serial_init(void)
+{
+       int ret;
+
+       /* register with serial core */
+       if ((ret = uart_register_driver(&ioc4_uart)) < 0) {
+               printk(KERN_WARNING
+                       "%s: Couldn't register IOC4 serial driver\n",
+                       __FUNCTION__);
+               return ret;
+       }
+       return 0;
+}
+
+MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) <pfg@sgi.com>");
+MODULE_DESCRIPTION("Serial PCI driver module for SGI IOC4 Base-IO Card");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(ioc4_serial_init);
+EXPORT_SYMBOL(ioc4_serial_attach_one);
diff --git a/drivers/serial/jsm/jsm.h b/drivers/serial/jsm/jsm.h
new file mode 100644 (file)
index 0000000..777829f
--- /dev/null
@@ -0,0 +1,398 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM 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, EXPRESS OR IMPLIED; 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.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+
+#ifndef __JSM_DRIVER_H
+#define __JSM_DRIVER_H
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/types.h>       /* To pick up the varions Linux types */
+#include <linux/tty.h>
+#include <linux/serial_core.h>
+#include <linux/device.h>
+
+/*
+ * Debugging levels can be set using debug insmod variable
+ * They can also be compiled out completely.
+ */
+enum {
+       DBG_INIT        = 0x01,
+       DBG_BASIC       = 0x02,
+       DBG_CORE        = 0x04,
+       DBG_OPEN        = 0x08,
+       DBG_CLOSE       = 0x10,
+       DBG_READ        = 0x20,
+       DBG_WRITE       = 0x40,
+       DBG_IOCTL       = 0x80,
+       DBG_PROC        = 0x100,
+       DBG_PARAM       = 0x200,
+       DBG_PSCAN       = 0x400,
+       DBG_EVENT       = 0x800,
+       DBG_DRAIN       = 0x1000,
+       DBG_MSIGS       = 0x2000,
+       DBG_MGMT        = 0x4000,
+       DBG_INTR        = 0x8000,
+       DBG_CARR        = 0x10000,
+};
+
+#define jsm_printk(nlevel, klevel, pdev, fmt, args...) \
+       if ((DBG_##nlevel & jsm_debug))                 \
+       dev_printk(KERN_##klevel, pdev->dev, fmt, ## args)
+
+#define MAXPORTS       8
+#define MAX_STOPS_SENT 5
+
+/* Board type definitions */
+
+#define T_NEO          0000
+#define T_CLASSIC      0001
+#define T_PCIBUS       0400
+
+/* Board State Definitions */
+
+#define BD_RUNNING     0x0
+#define BD_REASON      0x7f
+#define BD_NOTFOUND    0x1
+#define BD_NOIOPORT    0x2
+#define BD_NOMEM       0x3
+#define BD_NOBIOS      0x4
+#define BD_NOFEP       0x5
+#define BD_FAILED      0x6
+#define BD_ALLOCATED   0x7
+#define BD_TRIBOOT     0x8
+#define BD_BADKME      0x80
+
+
+/* 4 extra for alignment play space */
+#define WRITEBUFLEN    ((4096) + 4)
+#define MYFLIPLEN      N_TTY_BUF_SIZE
+
+#define JSM_VERSION    "jsm: 1.1-1-INKERNEL"
+#define JSM_PARTNUM    "40002438_A-INKERNEL"
+
+struct jsm_board;
+struct jsm_channel;
+
+/************************************************************************
+ * Per board operations structure                                      *
+ ************************************************************************/
+struct board_ops {
+       irqreturn_t (*intr) (int irq, void *voidbrd, struct pt_regs *regs);
+       void (*uart_init) (struct jsm_channel *ch);
+       void (*uart_off) (struct jsm_channel *ch);
+       void (*param) (struct jsm_channel *ch);
+       void (*assert_modem_signals) (struct jsm_channel *ch);
+       void (*flush_uart_write) (struct jsm_channel *ch);
+       void (*flush_uart_read) (struct jsm_channel *ch);
+       void (*disable_receiver) (struct jsm_channel *ch);
+       void (*enable_receiver) (struct jsm_channel *ch);
+       void (*send_break) (struct jsm_channel *ch);
+       void (*clear_break) (struct jsm_channel *ch, int);
+       void (*send_start_character) (struct jsm_channel *ch);
+       void (*send_stop_character) (struct jsm_channel *ch);
+       void (*copy_data_from_queue_to_uart) (struct jsm_channel *ch);
+       u32 (*get_uart_bytes_left) (struct jsm_channel *ch);
+       void (*send_immediate_char) (struct jsm_channel *ch, unsigned char);
+};
+
+
+/*
+ *     Per-board information
+ */
+struct jsm_board
+{
+       int             boardnum;       /* Board number: 0-32 */
+
+       int             type;           /* Type of board */
+       u8              rev;            /* PCI revision ID */
+       struct pci_dev  *pci_dev;
+       u32             maxports;       /* MAX ports this board can handle */
+
+       spinlock_t      bd_lock;        /* Used to protect board */
+
+       spinlock_t      bd_intr_lock;   /* Used to protect the poller tasklet and
+                                        * the interrupt routine from each other.
+                                        */
+
+       u32             nasync;         /* Number of ports on card */
+
+       u32             irq;            /* Interrupt request number */
+       u64             intr_count;     /* Count of interrupts */
+
+       u64             membase;        /* Start of base memory of the card */
+       u64             membase_end;    /* End of base memory of the card */
+
+       u8      __iomem *re_map_membase;/* Remapped memory of the card */
+
+       u64             iobase;         /* Start of io base of the card */
+       u64             iobase_end;     /* End of io base of the card */
+
+       u32             bd_uart_offset; /* Space between each UART */
+
+       struct jsm_channel *channels[MAXPORTS]; /* array of pointers to our channels. */
+       char            *flipbuf;       /* Our flip buffer, alloced if board is found */
+
+       u32             bd_dividend;    /* Board/UARTs specific dividend */
+
+       struct board_ops *bd_ops;
+
+       struct list_head jsm_board_entry;
+};
+
+/************************************************************************
+ * Device flag definitions for ch_flags.
+ ************************************************************************/
+#define CH_PRON                0x0001          /* Printer on string            */
+#define CH_STOP                0x0002          /* Output is stopped            */
+#define CH_STOPI       0x0004          /* Input is stopped             */
+#define CH_CD          0x0008          /* Carrier is present           */
+#define CH_FCAR                0x0010          /* Carrier forced on            */
+#define CH_HANGUP      0x0020          /* Hangup received              */
+
+#define CH_RECEIVER_OFF        0x0040          /* Receiver is off              */
+#define CH_OPENING     0x0080          /* Port in fragile open state   */
+#define CH_CLOSING     0x0100          /* Port in fragile close state  */
+#define CH_FIFO_ENABLED 0x0200         /* Port has FIFOs enabled       */
+#define CH_TX_FIFO_EMPTY 0x0400                /* TX Fifo is completely empty  */
+#define CH_TX_FIFO_LWM 0x0800          /* TX Fifo is below Low Water   */
+#define CH_BREAK_SENDING 0x1000                /* Break is being sent          */
+#define CH_LOOPBACK 0x2000             /* Channel is in lookback mode  */
+#define CH_FLIPBUF_IN_USE 0x4000       /* Channel's flipbuf is in use  */
+#define CH_BAUD0       0x08000         /* Used for checking B0 transitions */
+
+/* Our Read/Error/Write queue sizes */
+#define RQUEUEMASK     0x1FFF          /* 8 K - 1 */
+#define EQUEUEMASK     0x1FFF          /* 8 K - 1 */
+#define WQUEUEMASK     0x0FFF          /* 4 K - 1 */
+#define RQUEUESIZE     (RQUEUEMASK + 1)
+#define EQUEUESIZE     RQUEUESIZE
+#define WQUEUESIZE     (WQUEUEMASK + 1)
+
+
+/************************************************************************
+ * Channel information structure.
+ ************************************************************************/
+struct jsm_channel {
+       struct uart_port uart_port;
+       struct jsm_board        *ch_bd;         /* Board structure pointer      */
+
+       spinlock_t      ch_lock;        /* provide for serialization */
+       wait_queue_head_t ch_flags_wait;
+
+       u32             ch_portnum;     /* Port number, 0 offset.       */
+       u32             ch_open_count;  /* open count                   */
+       u32             ch_flags;       /* Channel flags                */
+
+       u64             ch_close_delay; /* How long we should drop RTS/DTR for */
+
+       u64             ch_cpstime;     /* Time for CPS calculations    */
+
+       tcflag_t        ch_c_iflag;     /* channel iflags               */
+       tcflag_t        ch_c_cflag;     /* channel cflags               */
+       tcflag_t        ch_c_oflag;     /* channel oflags               */
+       tcflag_t        ch_c_lflag;     /* channel lflags               */
+       u8              ch_stopc;       /* Stop character               */
+       u8              ch_startc;      /* Start character              */
+
+       u32             ch_old_baud;    /* Cache of the current baud */
+       u32             ch_custom_speed;/* Custom baud, if set */
+
+       u32             ch_wopen;       /* Waiting for open process cnt */
+
+       u8              ch_mostat;      /* FEP output modem status      */
+       u8              ch_mistat;      /* FEP input modem status       */
+
+       struct neo_uart_struct __iomem *ch_neo_uart;    /* Pointer to the "mapped" UART struct */
+       u8              ch_cached_lsr;  /* Cached value of the LSR register */
+
+       u8              *ch_rqueue;     /* Our read queue buffer - malloc'ed */
+       u16             ch_r_head;      /* Head location of the read queue */
+       u16             ch_r_tail;      /* Tail location of the read queue */
+
+       u8              *ch_equeue;     /* Our error queue buffer - malloc'ed */
+       u16             ch_e_head;      /* Head location of the error queue */
+       u16             ch_e_tail;      /* Tail location of the error queue */
+
+       u8              *ch_wqueue;     /* Our write queue buffer - malloc'ed */
+       u16             ch_w_head;      /* Head location of the write queue */
+       u16             ch_w_tail;      /* Tail location of the write queue */
+
+       u64             ch_rxcount;     /* total of data received so far */
+       u64             ch_txcount;     /* total of data transmitted so far */
+
+       u8              ch_r_tlevel;    /* Receive Trigger level */
+       u8              ch_t_tlevel;    /* Transmit Trigger level */
+
+       u8              ch_r_watermark; /* Receive Watermark */
+
+
+       u32             ch_stops_sent;  /* How many times I have sent a stop character
+                                        * to try to stop the other guy sending.
+                                        */
+       u64             ch_err_parity;  /* Count of parity errors on channel */
+       u64             ch_err_frame;   /* Count of framing errors on channel */
+       u64             ch_err_break;   /* Count of breaks on channel */
+       u64             ch_err_overrun; /* Count of overruns on channel */
+
+       u64             ch_xon_sends;   /* Count of xons transmitted */
+       u64             ch_xoff_sends;  /* Count of xoffs transmitted */
+};
+
+
+/************************************************************************
+ * Per channel/port NEO UART structure                                 *
+ ************************************************************************
+ *             Base Structure Entries Usage Meanings to Host           *
+ *                                                                     *
+ *     W = read write          R = read only                           *
+ *                     U = Unused.                                     *
+ ************************************************************************/
+
+struct neo_uart_struct {
+        u8 txrx;               /* WR   RHR/THR - Holding Reg */
+        u8 ier;                /* WR   IER - Interrupt Enable Reg */
+        u8 isr_fcr;            /* WR   ISR/FCR - Interrupt Status Reg/Fifo Control Reg */
+        u8 lcr;                /* WR   LCR - Line Control Reg */
+        u8 mcr;                /* WR   MCR - Modem Control Reg */
+        u8 lsr;                /* WR   LSR - Line Status Reg */
+        u8 msr;                /* WR   MSR - Modem Status Reg */
+        u8 spr;                /* WR   SPR - Scratch Pad Reg */
+        u8 fctr;               /* WR   FCTR - Feature Control Reg */
+        u8 efr;                /* WR   EFR - Enhanced Function Reg */
+        u8 tfifo;              /* WR   TXCNT/TXTRG - Transmit FIFO Reg */
+        u8 rfifo;              /* WR   RXCNT/RXTRG - Recieve FIFO Reg */
+        u8 xoffchar1;  /* WR   XOFF 1 - XOff Character 1 Reg */
+        u8 xoffchar2;  /* WR   XOFF 2 - XOff Character 2 Reg */
+        u8 xonchar1;   /* WR   XON 1 - Xon Character 1 Reg */
+        u8 xonchar2;   /* WR   XON 2 - XOn Character 2 Reg */
+
+        u8 reserved1[0x2ff - 0x200]; /* U      Reserved by Exar */
+        u8 txrxburst[64];      /* RW   64 bytes of RX/TX FIFO Data */
+        u8 reserved2[0x37f - 0x340]; /* U      Reserved by Exar */
+        u8 rxburst_with_errors[64];    /* R    64 bytes of RX FIFO Data + LSR */
+};
+
+/* Where to read the extended interrupt register (32bits instead of 8bits) */
+#define        UART_17158_POLL_ADDR_OFFSET     0x80
+
+/*
+ * These are the redefinitions for the FCTR on the XR17C158, since
+ * Exar made them different than their earlier design. (XR16C854)
+ */
+
+/* These are only applicable when table D is selected */
+#define UART_17158_FCTR_RTS_NODELAY    0x00
+#define UART_17158_FCTR_RTS_4DELAY     0x01
+#define UART_17158_FCTR_RTS_6DELAY     0x02
+#define UART_17158_FCTR_RTS_8DELAY     0x03
+#define UART_17158_FCTR_RTS_12DELAY    0x12
+#define UART_17158_FCTR_RTS_16DELAY    0x05
+#define UART_17158_FCTR_RTS_20DELAY    0x13
+#define UART_17158_FCTR_RTS_24DELAY    0x06
+#define UART_17158_FCTR_RTS_28DELAY    0x14
+#define UART_17158_FCTR_RTS_32DELAY    0x07
+#define UART_17158_FCTR_RTS_36DELAY    0x16
+#define UART_17158_FCTR_RTS_40DELAY    0x08
+#define UART_17158_FCTR_RTS_44DELAY    0x09
+#define UART_17158_FCTR_RTS_48DELAY    0x10
+#define UART_17158_FCTR_RTS_52DELAY    0x11
+
+#define UART_17158_FCTR_RTS_IRDA       0x10
+#define UART_17158_FCTR_RS485          0x20
+#define UART_17158_FCTR_TRGA           0x00
+#define UART_17158_FCTR_TRGB           0x40
+#define UART_17158_FCTR_TRGC           0x80
+#define UART_17158_FCTR_TRGD           0xC0
+
+/* 17158 trigger table selects.. */
+#define UART_17158_FCTR_BIT6           0x40
+#define UART_17158_FCTR_BIT7           0x80
+
+/* 17158 TX/RX memmapped buffer offsets */
+#define UART_17158_RX_FIFOSIZE         64
+#define UART_17158_TX_FIFOSIZE         64
+
+/* 17158 Extended IIR's */
+#define UART_17158_IIR_RDI_TIMEOUT     0x0C    /* Receiver data TIMEOUT */
+#define UART_17158_IIR_XONXOFF         0x10    /* Received an XON/XOFF char */
+#define UART_17158_IIR_HWFLOW_STATE_CHANGE 0x20        /* CTS/DSR or RTS/DTR state change */
+#define UART_17158_IIR_FIFO_ENABLED    0xC0    /* 16550 FIFOs are Enabled */
+
+/*
+ * These are the extended interrupts that get sent
+ * back to us from the UART's 32bit interrupt register
+ */
+#define UART_17158_RX_LINE_STATUS      0x1     /* RX Ready */
+#define UART_17158_RXRDY_TIMEOUT       0x2     /* RX Ready Timeout */
+#define UART_17158_TXRDY               0x3     /* TX Ready */
+#define UART_17158_MSR                 0x4     /* Modem State Change */
+#define UART_17158_TX_AND_FIFO_CLR     0x40    /* Transmitter Holding Reg Empty */
+#define UART_17158_RX_FIFO_DATA_ERROR  0x80    /* UART detected an RX FIFO Data error */
+
+/*
+ * These are the EXTENDED definitions for the 17C158's Interrupt
+ * Enable Register.
+ */
+#define UART_17158_EFR_ECB     0x10    /* Enhanced control bit */
+#define UART_17158_EFR_IXON    0x2     /* Receiver compares Xon1/Xoff1 */
+#define UART_17158_EFR_IXOFF   0x8     /* Transmit Xon1/Xoff1 */
+#define UART_17158_EFR_RTSDTR  0x40    /* Auto RTS/DTR Flow Control Enable */
+#define UART_17158_EFR_CTSDSR  0x80    /* Auto CTS/DSR Flow COntrol Enable */
+
+#define UART_17158_XOFF_DETECT 0x1     /* Indicates whether chip saw an incoming XOFF char */
+#define UART_17158_XON_DETECT  0x2     /* Indicates whether chip saw an incoming XON char */
+
+#define UART_17158_IER_RSVD1   0x10    /* Reserved by Exar */
+#define UART_17158_IER_XOFF    0x20    /* Xoff Interrupt Enable */
+#define UART_17158_IER_RTSDTR  0x40    /* Output Interrupt Enable */
+#define UART_17158_IER_CTSDSR  0x80    /* Input Interrupt Enable */
+
+#define PCI_DEVICE_NEO_2DB9_PCI_NAME           "Neo 2 - DB9 Universal PCI"
+#define PCI_DEVICE_NEO_2DB9PRI_PCI_NAME                "Neo 2 - DB9 Universal PCI - Powered Ring Indicator"
+#define PCI_DEVICE_NEO_2RJ45_PCI_NAME          "Neo 2 - RJ45 Universal PCI"
+#define PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME       "Neo 2 - RJ45 Universal PCI - Powered Ring Indicator"
+
+/*
+ * Our Global Variables.
+ */
+extern struct  uart_driver jsm_uart_driver;
+extern struct  board_ops jsm_neo_ops;
+extern int     jsm_debug;
+extern int     jsm_rawreadok;
+
+/*************************************************************************
+ *
+ * Prototypes for non-static functions used in more than one module
+ *
+ *************************************************************************/
+int jsm_tty_write(struct uart_port *port);
+int jsm_tty_init(struct jsm_board *);
+int jsm_uart_port_init(struct jsm_board *);
+int jsm_remove_uart_port(struct jsm_board *);
+void jsm_input(struct jsm_channel *ch);
+void jsm_check_queue_flow_control(struct jsm_channel *ch);
+
+#endif
diff --git a/drivers/serial/jsm/jsm_driver.c b/drivers/serial/jsm/jsm_driver.c
new file mode 100644 (file)
index 0000000..cc5d213
--- /dev/null
@@ -0,0 +1,246 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM 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, EXPRESS OR IMPLIED; 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.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+
+#include "jsm.h"
+
+MODULE_AUTHOR("Digi International, http://www.digi.com");
+MODULE_DESCRIPTION("Driver for the Digi International "
+                  "Neo PCI based product line");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("jsm");
+
+#define JSM_DRIVER_NAME "jsm"
+#define NR_PORTS       32
+#define JSM_MINOR_START        0
+
+struct uart_driver jsm_uart_driver = {
+       .owner          = THIS_MODULE,
+       .driver_name    = JSM_DRIVER_NAME,
+       .dev_name       = "ttyn",
+       .major          = 253,
+       .minor          = JSM_MINOR_START,
+       .nr             = NR_PORTS,
+};
+
+int jsm_debug;
+int jsm_rawreadok;
+module_param(jsm_debug, int, 0);
+module_param(jsm_rawreadok, int, 0);
+MODULE_PARM_DESC(jsm_debug, "Driver debugging level");
+MODULE_PARM_DESC(jsm_rawreadok, "Bypass flip buffers on input");
+
+static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       int rc = 0;
+       struct jsm_board *brd;
+       static int adapter_count = 0;
+       int retval;
+
+       rc = pci_enable_device(pdev);
+       if (rc) {
+               dev_err(&pdev->dev, "Device enable FAILED\n");
+               goto out;
+       }
+
+       rc = pci_request_regions(pdev, "jsm");
+       if (rc) {
+               dev_err(&pdev->dev, "pci_request_region FAILED\n");
+               goto out_disable_device;
+       }
+
+       brd = kmalloc(sizeof(struct jsm_board), GFP_KERNEL);
+       if (!brd) {
+               dev_err(&pdev->dev,
+                       "memory allocation for board structure failed\n");
+               rc = -ENOMEM;
+               goto out_release_regions;
+       }
+       memset(brd, 0, sizeof(struct jsm_board));
+
+       /* store the info for the board we've found */
+       brd->boardnum = adapter_count++;
+       brd->pci_dev = pdev;
+       brd->maxports = 2;
+
+       spin_lock_init(&brd->bd_lock);
+       spin_lock_init(&brd->bd_intr_lock);
+
+       /* store which revision we have */
+       pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev);
+
+       brd->irq = pdev->irq;
+
+       jsm_printk(INIT, INFO, &brd->pci_dev,
+               "jsm_found_board - NEO adapter\n");
+
+       /* get the PCI Base Address Registers */
+       brd->membase    = pci_resource_start(pdev, 0);
+       brd->membase_end = pci_resource_end(pdev, 0);
+
+       if (brd->membase & 1)
+               brd->membase &= ~3;
+       else
+               brd->membase &= ~15;
+
+       /* Assign the board_ops struct */
+       brd->bd_ops = &jsm_neo_ops;
+
+       brd->bd_uart_offset = 0x200;
+       brd->bd_dividend = 921600;
+
+       brd->re_map_membase = ioremap(brd->membase, 0x1000);
+       if (!brd->re_map_membase) {
+               dev_err(&pdev->dev,
+                       "card has no PCI Memory resources, "
+                       "failing board.\n");
+               rc = -ENOMEM;
+               goto out_kfree_brd;
+       }
+
+       rc = request_irq(brd->irq, brd->bd_ops->intr,
+                       SA_INTERRUPT|SA_SHIRQ, "JSM", brd);
+       if (rc) {
+               printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq);
+               goto out_iounmap;
+       }
+
+       rc = jsm_tty_init(brd);
+       if (rc < 0) {
+               dev_err(&pdev->dev, "Can't init tty devices (%d)\n", rc);
+               retval = -ENXIO;
+               goto out_free_irq;
+       }
+
+       rc = jsm_uart_port_init(brd);
+       if (rc < 0) {
+               /* XXX: leaking all resources from jsm_tty_init here! */
+               dev_err(&pdev->dev, "Can't init uart port (%d)\n", rc);
+               retval = -ENXIO;
+               goto out_free_irq;
+       }
+
+       /* Log the information about the board */
+       dev_info(&pdev->dev, "board %d: Digi Neo (rev %d), irq %d\n",
+                       adapter_count, brd->rev, brd->irq);
+
+       /*
+        * allocate flip buffer for board.
+        *
+        * Okay to malloc with GFP_KERNEL, we are not at interrupt
+        * context, and there are no locks held.
+        */
+       brd->flipbuf = kmalloc(MYFLIPLEN, GFP_KERNEL);
+       if (!brd->flipbuf) {
+               /* XXX: leaking all resources from jsm_tty_init and
+                       jsm_uart_port_init here! */
+               dev_err(&pdev->dev, "memory allocation for flipbuf failed\n");
+               retval = -ENOMEM;
+               goto out_free_irq;
+       }
+       memset(brd->flipbuf, 0, MYFLIPLEN);
+
+       pci_set_drvdata(pdev, brd);
+
+       return 0;
+ out_free_irq:
+       free_irq(brd->irq, brd);
+ out_iounmap:
+       iounmap(brd->re_map_membase);
+ out_kfree_brd:
+       kfree(brd);
+ out_release_regions:
+       pci_release_regions(pdev);
+ out_disable_device:
+       pci_disable_device(pdev);
+ out:
+       return rc;
+}
+
+static void jsm_remove_one(struct pci_dev *pdev)
+{
+       struct jsm_board *brd = pci_get_drvdata(pdev);
+       int i = 0;
+
+       jsm_remove_uart_port(brd);
+
+       free_irq(brd->irq, brd);
+       iounmap(brd->re_map_membase);
+
+       /* Free all allocated channels structs */
+       for (i = 0; i < brd->maxports; i++) {
+               if (brd->channels[i]) {
+                       kfree(brd->channels[i]->ch_rqueue);
+                       kfree(brd->channels[i]->ch_equeue);
+                       kfree(brd->channels[i]->ch_wqueue);
+                       kfree(brd->channels[i]);
+               }
+       }
+
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+       kfree(brd->flipbuf);
+       kfree(brd);
+}
+
+static struct pci_device_id jsm_pci_tbl[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9), 0, 0, 0 },
+       { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9PRI), 0, 0, 1 },
+       { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45), 0, 0, 2 },
+       { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI), 0, 0, 3 },
+       { 0, }
+};
+MODULE_DEVICE_TABLE(pci, jsm_pci_tbl);
+
+static struct pci_driver jsm_driver = {
+       .name           = "jsm",
+       .id_table       = jsm_pci_tbl,
+       .probe          = jsm_probe_one,
+       .remove         = __devexit_p(jsm_remove_one),
+};
+
+static int __init jsm_init_module(void)
+{
+       int rc;
+
+       rc = uart_register_driver(&jsm_uart_driver);
+       if (!rc) {
+               rc = pci_register_driver(&jsm_driver);
+               if (rc)
+                       uart_unregister_driver(&jsm_uart_driver);
+       }
+       return rc;
+}
+
+static void __exit jsm_exit_module(void)
+{
+       pci_unregister_driver(&jsm_driver);
+       uart_unregister_driver(&jsm_uart_driver);
+}
+
+module_init(jsm_init_module);
+module_exit(jsm_exit_module);
diff --git a/drivers/serial/jsm/jsm_neo.c b/drivers/serial/jsm/jsm_neo.c
new file mode 100644 (file)
index 0000000..3a11a69
--- /dev/null
@@ -0,0 +1,1427 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM 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, EXPRESS OR IMPLIED; 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.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+#include <linux/delay.h>       /* For udelay */
+#include <linux/serial_reg.h>  /* For the various UART offsets */
+#include <linux/tty.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+#include "jsm.h"               /* Driver main header file */
+
+static u32 jsm_offset_table[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+
+/*
+ * This function allows calls to ensure that all outstanding
+ * PCI writes have been completed, by doing a PCI read against
+ * a non-destructive, read-only location on the Neo card.
+ *
+ * In this case, we are reading the DVID (Read-only Device Identification)
+ * value of the Neo card.
+ */
+static inline void neo_pci_posting_flush(struct jsm_board *bd)
+{
+      readb(bd->re_map_membase + 0x8D);
+}
+
+static void neo_set_cts_flow_control(struct jsm_channel *ch)
+{
+       u8 ier = readb(&ch->ch_neo_uart->ier);
+       u8 efr = readb(&ch->ch_neo_uart->efr);
+
+       jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting CTSFLOW\n");
+
+       /* Turn on auto CTS flow control */
+       ier |= (UART_17158_IER_CTSDSR);
+       efr |= (UART_17158_EFR_ECB | UART_17158_EFR_CTSDSR);
+
+       /* Turn off auto Xon flow control */
+       efr &= ~(UART_17158_EFR_IXON);
+
+       /* Why? Becuz Exar's spec says we have to zero it out before setting it */
+       writeb(0, &ch->ch_neo_uart->efr);
+
+       /* Turn on UART enhanced bits */
+       writeb(efr, &ch->ch_neo_uart->efr);
+
+       /* Turn on table D, with 8 char hi/low watermarks */
+       writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr);
+
+       /* Feed the UART our trigger levels */
+       writeb(8, &ch->ch_neo_uart->tfifo);
+       ch->ch_t_tlevel = 8;
+
+       writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_rts_flow_control(struct jsm_channel *ch)
+{
+       u8 ier = readb(&ch->ch_neo_uart->ier);
+       u8 efr = readb(&ch->ch_neo_uart->efr);
+
+       jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting RTSFLOW\n");
+
+       /* Turn on auto RTS flow control */
+       ier |= (UART_17158_IER_RTSDTR);
+       efr |= (UART_17158_EFR_ECB | UART_17158_EFR_RTSDTR);
+
+       /* Turn off auto Xoff flow control */
+       ier &= ~(UART_17158_IER_XOFF);
+       efr &= ~(UART_17158_EFR_IXOFF);
+
+       /* Why? Becuz Exar's spec says we have to zero it out before setting it */
+       writeb(0, &ch->ch_neo_uart->efr);
+
+       /* Turn on UART enhanced bits */
+       writeb(efr, &ch->ch_neo_uart->efr);
+
+       writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr);
+       ch->ch_r_watermark = 4;
+
+       writeb(56, &ch->ch_neo_uart->rfifo);
+       ch->ch_r_tlevel = 56;
+
+       writeb(ier, &ch->ch_neo_uart->ier);
+
+       /*
+        * From the Neo UART spec sheet:
+        * The auto RTS/DTR function must be started by asserting
+        * RTS/DTR# output pin (MCR bit-0 or 1 to logic 1 after
+        * it is enabled.
+        */
+       ch->ch_mostat |= (UART_MCR_RTS);
+}
+
+
+static void neo_set_ixon_flow_control(struct jsm_channel *ch)
+{
+       u8 ier = readb(&ch->ch_neo_uart->ier);
+       u8 efr = readb(&ch->ch_neo_uart->efr);
+
+       jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXON FLOW\n");
+
+       /* Turn off auto CTS flow control */
+       ier &= ~(UART_17158_IER_CTSDSR);
+       efr &= ~(UART_17158_EFR_CTSDSR);
+
+       /* Turn on auto Xon flow control */
+       efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXON);
+
+       /* Why? Becuz Exar's spec says we have to zero it out before setting it */
+       writeb(0, &ch->ch_neo_uart->efr);
+
+       /* Turn on UART enhanced bits */
+       writeb(efr, &ch->ch_neo_uart->efr);
+
+       writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+       ch->ch_r_watermark = 4;
+
+       writeb(32, &ch->ch_neo_uart->rfifo);
+       ch->ch_r_tlevel = 32;
+
+       /* Tell UART what start/stop chars it should be looking for */
+       writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+       writeb(0, &ch->ch_neo_uart->xonchar2);
+
+       writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+       writeb(0, &ch->ch_neo_uart->xoffchar2);
+
+       writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_ixoff_flow_control(struct jsm_channel *ch)
+{
+       u8 ier = readb(&ch->ch_neo_uart->ier);
+       u8 efr = readb(&ch->ch_neo_uart->efr);
+
+       jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXOFF FLOW\n");
+
+       /* Turn off auto RTS flow control */
+       ier &= ~(UART_17158_IER_RTSDTR);
+       efr &= ~(UART_17158_EFR_RTSDTR);
+
+       /* Turn on auto Xoff flow control */
+       ier |= (UART_17158_IER_XOFF);
+       efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXOFF);
+
+       /* Why? Becuz Exar's spec says we have to zero it out before setting it */
+       writeb(0, &ch->ch_neo_uart->efr);
+
+       /* Turn on UART enhanced bits */
+       writeb(efr, &ch->ch_neo_uart->efr);
+
+       /* Turn on table D, with 8 char hi/low watermarks */
+       writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+       writeb(8, &ch->ch_neo_uart->tfifo);
+       ch->ch_t_tlevel = 8;
+
+       /* Tell UART what start/stop chars it should be looking for */
+       writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+       writeb(0, &ch->ch_neo_uart->xonchar2);
+
+       writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+       writeb(0, &ch->ch_neo_uart->xoffchar2);
+
+       writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_no_input_flow_control(struct jsm_channel *ch)
+{
+       u8 ier = readb(&ch->ch_neo_uart->ier);
+       u8 efr = readb(&ch->ch_neo_uart->efr);
+
+       jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Input FLOW\n");
+
+       /* Turn off auto RTS flow control */
+       ier &= ~(UART_17158_IER_RTSDTR);
+       efr &= ~(UART_17158_EFR_RTSDTR);
+
+       /* Turn off auto Xoff flow control */
+       ier &= ~(UART_17158_IER_XOFF);
+       if (ch->ch_c_iflag & IXON)
+               efr &= ~(UART_17158_EFR_IXOFF);
+       else
+               efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXOFF);
+
+       /* Why? Becuz Exar's spec says we have to zero it out before setting it */
+       writeb(0, &ch->ch_neo_uart->efr);
+
+       /* Turn on UART enhanced bits */
+       writeb(efr, &ch->ch_neo_uart->efr);
+
+       /* Turn on table D, with 8 char hi/low watermarks */
+       writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+       ch->ch_r_watermark = 0;
+
+       writeb(16, &ch->ch_neo_uart->tfifo);
+       ch->ch_t_tlevel = 16;
+
+       writeb(16, &ch->ch_neo_uart->rfifo);
+       ch->ch_r_tlevel = 16;
+
+       writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_no_output_flow_control(struct jsm_channel *ch)
+{
+       u8 ier = readb(&ch->ch_neo_uart->ier);
+       u8 efr = readb(&ch->ch_neo_uart->efr);
+
+       jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Output FLOW\n");
+
+       /* Turn off auto CTS flow control */
+       ier &= ~(UART_17158_IER_CTSDSR);
+       efr &= ~(UART_17158_EFR_CTSDSR);
+
+       /* Turn off auto Xon flow control */
+       if (ch->ch_c_iflag & IXOFF)
+               efr &= ~(UART_17158_EFR_IXON);
+       else
+               efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXON);
+
+       /* Why? Becuz Exar's spec says we have to zero it out before setting it */
+       writeb(0, &ch->ch_neo_uart->efr);
+
+       /* Turn on UART enhanced bits */
+       writeb(efr, &ch->ch_neo_uart->efr);
+
+       /* Turn on table D, with 8 char hi/low watermarks */
+       writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+       ch->ch_r_watermark = 0;
+
+       writeb(16, &ch->ch_neo_uart->tfifo);
+       ch->ch_t_tlevel = 16;
+
+       writeb(16, &ch->ch_neo_uart->rfifo);
+       ch->ch_r_tlevel = 16;
+
+       writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static inline void neo_set_new_start_stop_chars(struct jsm_channel *ch)
+{
+
+       /* if hardware flow control is set, then skip this whole thing */
+       if (ch->ch_c_cflag & CRTSCTS)
+               return;
+
+       jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "start\n");
+
+       /* Tell UART what start/stop chars it should be looking for */
+       writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+       writeb(0, &ch->ch_neo_uart->xonchar2);
+
+       writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+       writeb(0, &ch->ch_neo_uart->xoffchar2);
+}
+
+static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch)
+{
+       int qleft = 0;
+       u8 linestatus = 0;
+       u8 error_mask = 0;
+       int n = 0;
+       int total = 0;
+       u16 head;
+       u16 tail;
+
+       if (!ch)
+               return;
+
+       /* cache head and tail of queue */
+       head = ch->ch_r_head & RQUEUEMASK;
+       tail = ch->ch_r_tail & RQUEUEMASK;
+
+       /* Get our cached LSR */
+       linestatus = ch->ch_cached_lsr;
+       ch->ch_cached_lsr = 0;
+
+       /* Store how much space we have left in the queue */
+       if ((qleft = tail - head - 1) < 0)
+               qleft += RQUEUEMASK + 1;
+
+       /*
+        * If the UART is not in FIFO mode, force the FIFO copy to
+        * NOT be run, by setting total to 0.
+        *
+        * On the other hand, if the UART IS in FIFO mode, then ask
+        * the UART to give us an approximation of data it has RX'ed.
+        */
+       if (!(ch->ch_flags & CH_FIFO_ENABLED))
+               total = 0;
+       else {
+               total = readb(&ch->ch_neo_uart->rfifo);
+
+               /*
+                * EXAR chip bug - RX FIFO COUNT - Fudge factor.
+                *
+                * This resolves a problem/bug with the Exar chip that sometimes
+                * returns a bogus value in the rfifo register.
+                * The count can be any where from 0-3 bytes "off".
+                * Bizarre, but true.
+                */
+               total -= 3;
+       }
+
+       /*
+        * Finally, bound the copy to make sure we don't overflow
+        * our own queue...
+        * The byte by byte copy loop below this loop this will
+        * deal with the queue overflow possibility.
+        */
+       total = min(total, qleft);
+
+       while (total > 0) {
+               /*
+                * Grab the linestatus register, we need to check
+                * to see if there are any errors in the FIFO.
+                */
+               linestatus = readb(&ch->ch_neo_uart->lsr);
+
+               /*
+                * Break out if there is a FIFO error somewhere.
+                * This will allow us to go byte by byte down below,
+                * finding the exact location of the error.
+                */
+               if (linestatus & UART_17158_RX_FIFO_DATA_ERROR)
+                       break;
+
+               /* Make sure we don't go over the end of our queue */
+               n = min(((u32) total), (RQUEUESIZE - (u32) head));
+
+               /*
+                * Cut down n even further if needed, this is to fix
+                * a problem with memcpy_fromio() with the Neo on the
+                * IBM pSeries platform.
+                * 15 bytes max appears to be the magic number.
+                */
+               n = min((u32) n, (u32) 12);
+
+               /*
+                * Since we are grabbing the linestatus register, which
+                * will reset some bits after our read, we need to ensure
+                * we don't miss our TX FIFO emptys.
+                */
+               if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR))
+                       ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+
+               linestatus = 0;
+
+               /* Copy data from uart to the queue */
+               memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, n);
+               /*
+                * Since RX_FIFO_DATA_ERROR was 0, we are guarenteed
+                * that all the data currently in the FIFO is free of
+                * breaks and parity/frame/orun errors.
+                */
+               memset(ch->ch_equeue + head, 0, n);
+
+               /* Add to and flip head if needed */
+               head = (head + n) & RQUEUEMASK;
+               total -= n;
+               qleft -= n;
+               ch->ch_rxcount += n;
+       }
+
+       /*
+        * Create a mask to determine whether we should
+        * insert the character (if any) into our queue.
+        */
+       if (ch->ch_c_iflag & IGNBRK)
+               error_mask |= UART_LSR_BI;
+
+       /*
+        * Now cleanup any leftover bytes still in the UART.
+        * Also deal with any possible queue overflow here as well.
+        */
+       while (1) {
+
+               /*
+                * Its possible we have a linestatus from the loop above
+                * this, so we "OR" on any extra bits.
+                */
+               linestatus |= readb(&ch->ch_neo_uart->lsr);
+
+               /*
+                * If the chip tells us there is no more data pending to
+                * be read, we can then leave.
+                * But before we do, cache the linestatus, just in case.
+                */
+               if (!(linestatus & UART_LSR_DR)) {
+                       ch->ch_cached_lsr = linestatus;
+                       break;
+               }
+
+               /* No need to store this bit */
+               linestatus &= ~UART_LSR_DR;
+
+               /*
+                * Since we are grabbing the linestatus register, which
+                * will reset some bits after our read, we need to ensure
+                * we don't miss our TX FIFO emptys.
+                */
+               if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR)) {
+                       linestatus &= ~(UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR);
+                       ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+               }
+
+               /*
+                * Discard character if we are ignoring the error mask.
+                */
+               if (linestatus & error_mask) {
+                       u8 discard;
+                       linestatus = 0;
+                       memcpy_fromio(&discard, &ch->ch_neo_uart->txrxburst, 1);
+                       continue;
+               }
+
+               /*
+                * If our queue is full, we have no choice but to drop some data.
+                * The assumption is that HWFLOW or SWFLOW should have stopped
+                * things way way before we got to this point.
+                *
+                * I decided that I wanted to ditch the oldest data first,
+                * I hope thats okay with everyone? Yes? Good.
+                */
+               while (qleft < 1) {
+                       jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+                               "Queue full, dropping DATA:%x LSR:%x\n",
+                               ch->ch_rqueue[tail], ch->ch_equeue[tail]);
+
+                       ch->ch_r_tail = tail = (tail + 1) & RQUEUEMASK;
+                       ch->ch_err_overrun++;
+                       qleft++;
+               }
+
+               memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, 1);
+               ch->ch_equeue[head] = (u8) linestatus;
+
+               jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+                               "DATA/LSR pair: %x %x\n", ch->ch_rqueue[head], ch->ch_equeue[head]);
+
+               /* Ditch any remaining linestatus value. */
+               linestatus = 0;
+
+               /* Add to and flip head if needed */
+               head = (head + 1) & RQUEUEMASK;
+
+               qleft--;
+               ch->ch_rxcount++;
+       }
+
+       /*
+        * Write new final heads to channel structure.
+        */
+       ch->ch_r_head = head & RQUEUEMASK;
+       ch->ch_e_head = head & EQUEUEMASK;
+       jsm_input(ch);
+}
+
+static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
+{
+       u16 head;
+       u16 tail;
+       int n;
+       int s;
+       int qlen;
+       u32 len_written = 0;
+
+       if (!ch)
+               return;
+
+       /* No data to write to the UART */
+       if (ch->ch_w_tail == ch->ch_w_head)
+               return;
+
+       /* If port is "stopped", don't send any data to the UART */
+       if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_BREAK_SENDING))
+               return;
+       /*
+        * If FIFOs are disabled. Send data directly to txrx register
+        */
+       if (!(ch->ch_flags & CH_FIFO_ENABLED)) {
+               u8 lsrbits = readb(&ch->ch_neo_uart->lsr);
+
+               ch->ch_cached_lsr |= lsrbits;
+               if (ch->ch_cached_lsr & UART_LSR_THRE) {
+                       ch->ch_cached_lsr &= ~(UART_LSR_THRE);
+
+                       writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_neo_uart->txrx);
+                       jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev,
+                                       "Tx data: %x\n", ch->ch_wqueue[ch->ch_w_head]);
+                       ch->ch_w_tail++;
+                       ch->ch_w_tail &= WQUEUEMASK;
+                       ch->ch_txcount++;
+               }
+               return;
+       }
+
+       /*
+        * We have to do it this way, because of the EXAR TXFIFO count bug.
+        */
+       if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM)))
+               return;
+
+       len_written = 0;
+       n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel;
+
+       /* cache head and tail of queue */
+       head = ch->ch_w_head & WQUEUEMASK;
+       tail = ch->ch_w_tail & WQUEUEMASK;
+       qlen = (head - tail) & WQUEUEMASK;
+
+       /* Find minimum of the FIFO space, versus queue length */
+       n = min(n, qlen);
+
+       while (n > 0) {
+
+               s = ((head >= tail) ? head : WQUEUESIZE) - tail;
+               s = min(s, n);
+
+               if (s <= 0)
+                       break;
+
+               memcpy_toio(&ch->ch_neo_uart->txrxburst, ch->ch_wqueue + tail, s);
+               /* Add and flip queue if needed */
+               tail = (tail + s) & WQUEUEMASK;
+               n -= s;
+               ch->ch_txcount += s;
+               len_written += s;
+       }
+
+       /* Update the final tail */
+       ch->ch_w_tail = tail & WQUEUEMASK;
+
+       if (len_written >= ch->ch_t_tlevel)
+               ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+
+       if (!jsm_tty_write(&ch->uart_port))
+               uart_write_wakeup(&ch->uart_port);
+}
+
+static void neo_parse_modem(struct jsm_channel *ch, u8 signals)
+{
+       u8 msignals = signals;
+
+       jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev,
+                       "neo_parse_modem: port: %d msignals: %x\n", ch->ch_portnum, msignals);
+
+       if (!ch)
+               return;
+
+       /* Scrub off lower bits. They signify delta's, which I don't care about */
+       msignals &= 0xf0;
+
+       if (msignals & UART_MSR_DCD)
+               ch->ch_mistat |= UART_MSR_DCD;
+       else
+               ch->ch_mistat &= ~UART_MSR_DCD;
+
+       if (msignals & UART_MSR_DSR)
+               ch->ch_mistat |= UART_MSR_DSR;
+       else
+               ch->ch_mistat &= ~UART_MSR_DSR;
+
+       if (msignals & UART_MSR_RI)
+               ch->ch_mistat |= UART_MSR_RI;
+       else
+               ch->ch_mistat &= ~UART_MSR_RI;
+
+       if (msignals & UART_MSR_CTS)
+               ch->ch_mistat |= UART_MSR_CTS;
+       else
+               ch->ch_mistat &= ~UART_MSR_CTS;
+
+       jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev,
+                       "Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n",
+               ch->ch_portnum,
+               !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR),
+               !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS),
+               !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_CTS),
+               !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DSR),
+               !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_RI),
+               !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DCD));
+}
+
+/* Make the UART raise any of the output signals we want up */
+static void neo_assert_modem_signals(struct jsm_channel *ch)
+{
+       u8 out;
+
+       if (!ch)
+               return;
+
+       out = ch->ch_mostat;
+
+       writeb(out, &ch->ch_neo_uart->mcr);
+
+       /* flush write operation */
+       neo_pci_posting_flush(ch->ch_bd);
+}
+
+/*
+ * Flush the WRITE FIFO on the Neo.
+ *
+ * NOTE: Channel lock MUST be held before calling this function!
+ */
+static void neo_flush_uart_write(struct jsm_channel *ch)
+{
+       u8 tmp = 0;
+       int i = 0;
+
+       if (!ch)
+               return;
+
+       writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
+
+       for (i = 0; i < 10; i++) {
+
+               /* Check to see if the UART feels it completely flushed the FIFO. */
+               tmp = readb(&ch->ch_neo_uart->isr_fcr);
+               if (tmp & 4) {
+                       jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
+                                       "Still flushing TX UART... i: %d\n", i);
+                       udelay(10);
+               }
+               else
+                       break;
+       }
+
+       ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+}
+
+
+/*
+ * Flush the READ FIFO on the Neo.
+ *
+ * NOTE: Channel lock MUST be held before calling this function!
+ */
+static void neo_flush_uart_read(struct jsm_channel *ch)
+{
+       u8 tmp = 0;
+       int i = 0;
+
+       if (!ch)
+               return;
+
+       writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR), &ch->ch_neo_uart->isr_fcr);
+
+       for (i = 0; i < 10; i++) {
+
+               /* Check to see if the UART feels it completely flushed the FIFO. */
+               tmp = readb(&ch->ch_neo_uart->isr_fcr);
+               if (tmp & 2) {
+                       jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
+                                       "Still flushing RX UART... i: %d\n", i);
+                       udelay(10);
+               }
+               else
+                       break;
+       }
+}
+
+/*
+ * No locks are assumed to be held when calling this function.
+ */
+static void neo_clear_break(struct jsm_channel *ch, int force)
+{
+       unsigned long lock_flags;
+
+       spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+       /* Turn break off, and unset some variables */
+       if (ch->ch_flags & CH_BREAK_SENDING) {
+               u8 temp = readb(&ch->ch_neo_uart->lcr);
+               writeb((temp & ~UART_LCR_SBC), &ch->ch_neo_uart->lcr);
+
+               ch->ch_flags &= ~(CH_BREAK_SENDING);
+               jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
+                               "clear break Finishing UART_LCR_SBC! finished: %lx\n", jiffies);
+
+               /* flush write operation */
+               neo_pci_posting_flush(ch->ch_bd);
+       }
+       spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+}
+
+/*
+ * Parse the ISR register.
+ */
+static inline void neo_parse_isr(struct jsm_board *brd, u32 port)
+{
+       struct jsm_channel *ch;
+       u8 isr;
+       u8 cause;
+       unsigned long lock_flags;
+
+       if (!brd)
+               return;
+
+       if (port > brd->maxports)
+               return;
+
+       ch = brd->channels[port];
+       if (!ch)
+               return;
+
+       /* Here we try to figure out what caused the interrupt to happen */
+       while (1) {
+
+               isr = readb(&ch->ch_neo_uart->isr_fcr);
+
+               /* Bail if no pending interrupt */
+               if (isr & UART_IIR_NO_INT)
+                       break;
+
+               /*
+                * Yank off the upper 2 bits, which just show that the FIFO's are enabled.
+                */
+               isr &= ~(UART_17158_IIR_FIFO_ENABLED);
+
+               jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
+                               "%s:%d isr: %x\n", __FILE__, __LINE__, isr);
+
+               if (isr & (UART_17158_IIR_RDI_TIMEOUT | UART_IIR_RDI)) {
+                       /* Read data from uart -> queue */
+                       neo_copy_data_from_uart_to_queue(ch);
+
+                       /* Call our tty layer to enforce queue flow control if needed. */
+                       spin_lock_irqsave(&ch->ch_lock, lock_flags);
+                       jsm_check_queue_flow_control(ch);
+                       spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+               }
+
+               if (isr & UART_IIR_THRI) {
+                       /* Transfer data (if any) from Write Queue -> UART. */
+                       spin_lock_irqsave(&ch->ch_lock, lock_flags);
+                       ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+                       spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+                       neo_copy_data_from_queue_to_uart(ch);
+               }
+
+               if (isr & UART_17158_IIR_XONXOFF) {
+                       cause = readb(&ch->ch_neo_uart->xoffchar1);
+
+                       jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
+                                       "Port %d. Got ISR_XONXOFF: cause:%x\n", port, cause);
+
+                       /*
+                        * Since the UART detected either an XON or
+                        * XOFF match, we need to figure out which
+                        * one it was, so we can suspend or resume data flow.
+                        */
+                       spin_lock_irqsave(&ch->ch_lock, lock_flags);
+                       if (cause == UART_17158_XON_DETECT) {
+                               /* Is output stopped right now, if so, resume it */
+                               if (brd->channels[port]->ch_flags & CH_STOP) {
+                                       ch->ch_flags &= ~(CH_STOP);
+                               }
+                               jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
+                                               "Port %d. XON detected in incoming data\n", port);
+                       }
+                       else if (cause == UART_17158_XOFF_DETECT) {
+                               if (!(brd->channels[port]->ch_flags & CH_STOP)) {
+                                       ch->ch_flags |= CH_STOP;
+                                       jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
+                                                       "Setting CH_STOP\n");
+                               }
+                               jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
+                                               "Port: %d. XOFF detected in incoming data\n", port);
+                       }
+                       spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+               }
+
+               if (isr & UART_17158_IIR_HWFLOW_STATE_CHANGE) {
+                       /*
+                        * If we get here, this means the hardware is doing auto flow control.
+                        * Check to see whether RTS/DTR or CTS/DSR caused this interrupt.
+                        */
+                       cause = readb(&ch->ch_neo_uart->mcr);
+
+                       /* Which pin is doing auto flow? RTS or DTR? */
+                       spin_lock_irqsave(&ch->ch_lock, lock_flags);
+                       if ((cause & 0x4) == 0) {
+                               if (cause & UART_MCR_RTS)
+                                       ch->ch_mostat |= UART_MCR_RTS;
+                               else
+                                       ch->ch_mostat &= ~(UART_MCR_RTS);
+                       } else {
+                               if (cause & UART_MCR_DTR)
+                                       ch->ch_mostat |= UART_MCR_DTR;
+                               else
+                                       ch->ch_mostat &= ~(UART_MCR_DTR);
+                       }
+                       spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+               }
+
+               /* Parse any modem signal changes */
+               jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
+                               "MOD_STAT: sending to parse_modem_sigs\n");
+               neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
+       }
+}
+
+static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
+{
+       struct jsm_channel *ch;
+       int linestatus;
+       unsigned long lock_flags;
+
+       if (!brd)
+               return;
+
+       if (port > brd->maxports)
+               return;
+
+       ch = brd->channels[port];
+       if (!ch)
+               return;
+
+       linestatus = readb(&ch->ch_neo_uart->lsr);
+
+       jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
+                       "%s:%d port: %d linestatus: %x\n", __FILE__, __LINE__, port, linestatus);
+
+       ch->ch_cached_lsr |= linestatus;
+
+       if (ch->ch_cached_lsr & UART_LSR_DR) {
+               /* Read data from uart -> queue */
+               neo_copy_data_from_uart_to_queue(ch);
+               spin_lock_irqsave(&ch->ch_lock, lock_flags);
+               jsm_check_queue_flow_control(ch);
+               spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+       }
+
+       /*
+        * This is a special flag. It indicates that at least 1
+        * RX error (parity, framing, or break) has happened.
+        * Mark this in our struct, which will tell me that I have
+        *to do the special RX+LSR read for this FIFO load.
+        */
+       if (linestatus & UART_17158_RX_FIFO_DATA_ERROR)
+               jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
+                       "%s:%d Port: %d Got an RX error, need to parse LSR\n",
+                       __FILE__, __LINE__, port);
+
+       /*
+        * The next 3 tests should *NOT* happen, as the above test
+        * should encapsulate all 3... At least, thats what Exar says.
+        */
+
+       if (linestatus & UART_LSR_PE) {
+               ch->ch_err_parity++;
+               jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
+                       "%s:%d Port: %d. PAR ERR!\n", __FILE__, __LINE__, port);
+       }
+
+       if (linestatus & UART_LSR_FE) {
+               ch->ch_err_frame++;
+               jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
+                       "%s:%d Port: %d. FRM ERR!\n", __FILE__, __LINE__, port);
+       }
+
+       if (linestatus & UART_LSR_BI) {
+               ch->ch_err_break++;
+               jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
+                       "%s:%d Port: %d. BRK INTR!\n", __FILE__, __LINE__, port);
+       }
+
+       if (linestatus & UART_LSR_OE) {
+               /*
+                * Rx Oruns. Exar says that an orun will NOT corrupt
+                * the FIFO. It will just replace the holding register
+                * with this new data byte. So basically just ignore this.
+                * Probably we should eventually have an orun stat in our driver...
+                */
+               ch->ch_err_overrun++;
+               jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
+                       "%s:%d Port: %d. Rx Overrun!\n", __FILE__, __LINE__, port);
+       }
+
+       if (linestatus & UART_LSR_THRE) {
+               spin_lock_irqsave(&ch->ch_lock, lock_flags);
+               ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+               spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+               /* Transfer data (if any) from Write Queue -> UART. */
+               neo_copy_data_from_queue_to_uart(ch);
+       }
+       else if (linestatus & UART_17158_TX_AND_FIFO_CLR) {
+               spin_lock_irqsave(&ch->ch_lock, lock_flags);
+               ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+               spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+               /* Transfer data (if any) from Write Queue -> UART. */
+               neo_copy_data_from_queue_to_uart(ch);
+       }
+}
+
+/*
+ * neo_param()
+ * Send any/all changes to the line to the UART.
+ */
+static void neo_param(struct jsm_channel *ch)
+{
+       u8 lcr = 0;
+       u8 uart_lcr = 0;
+       u8 ier = 0;
+       u32 baud = 9600;
+       int quot = 0;
+       struct jsm_board *bd;
+
+       bd = ch->ch_bd;
+       if (!bd)
+               return;
+
+       /*
+        * If baud rate is zero, flush queues, and set mval to drop DTR.
+        */
+       if ((ch->ch_c_cflag & (CBAUD)) == 0) {
+               ch->ch_r_head = ch->ch_r_tail = 0;
+               ch->ch_e_head = ch->ch_e_tail = 0;
+               ch->ch_w_head = ch->ch_w_tail = 0;
+
+               neo_flush_uart_write(ch);
+               neo_flush_uart_read(ch);
+
+               ch->ch_flags |= (CH_BAUD0);
+               ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
+               neo_assert_modem_signals(ch);
+               ch->ch_old_baud = 0;
+               return;
+
+       } else if (ch->ch_custom_speed) {
+                       baud = ch->ch_custom_speed;
+                       if (ch->ch_flags & CH_BAUD0)
+                               ch->ch_flags &= ~(CH_BAUD0);
+               } else {
+                       int iindex = 0;
+                       int jindex = 0;
+
+                       const u64 bauds[4][16] = {
+                               {
+                                       0,      50,     75,     110,
+                                       134,    150,    200,    300,
+                                       600,    1200,   1800,   2400,
+                                       4800,   9600,   19200,  38400 },
+                               {
+                                       0,      57600,  115200, 230400,
+                                       460800, 150,    200,    921600,
+                                       600,    1200,   1800,   2400,
+                                       4800,   9600,   19200,  38400 },
+                               {
+                                       0,      57600,  76800, 115200,
+                                       131657, 153600, 230400, 460800,
+                                       921600, 1200,   1800,   2400,
+                                       4800,   9600,   19200,  38400 },
+                               {
+                                       0,      57600,  115200, 230400,
+                                       460800, 150,    200,    921600,
+                                       600,    1200,   1800,   2400,
+                                       4800,   9600,   19200,  38400 }
+                       };
+
+                       baud = C_BAUD(ch->uart_port.info->tty) & 0xff;
+
+                       if (ch->ch_c_cflag & CBAUDEX)
+                               iindex = 1;
+
+                       jindex = baud;
+
+                       if ((iindex >= 0) && (iindex < 4) && (jindex >= 0) && (jindex < 16))
+                               baud = bauds[iindex][jindex];
+                       else {
+                               jsm_printk(IOCTL, DEBUG, &ch->ch_bd->pci_dev,
+                                       "baud indices were out of range (%d)(%d)",
+                               iindex, jindex);
+                               baud = 0;
+                       }
+
+                       if (baud == 0)
+                               baud = 9600;
+
+                       if (ch->ch_flags & CH_BAUD0)
+                               ch->ch_flags &= ~(CH_BAUD0);
+               }
+
+       if (ch->ch_c_cflag & PARENB)
+               lcr |= UART_LCR_PARITY;
+
+       if (!(ch->ch_c_cflag & PARODD))
+               lcr |= UART_LCR_EPAR;
+
+       /*
+        * Not all platforms support mark/space parity,
+        * so this will hide behind an ifdef.
+        */
+#ifdef CMSPAR
+       if (ch->ch_c_cflag & CMSPAR)
+               lcr |= UART_LCR_SPAR;
+#endif
+
+       if (ch->ch_c_cflag & CSTOPB)
+               lcr |= UART_LCR_STOP;
+
+       switch (ch->ch_c_cflag & CSIZE) {
+               case CS5:
+                       lcr |= UART_LCR_WLEN5;
+                       break;
+               case CS6:
+                       lcr |= UART_LCR_WLEN6;
+                       break;
+               case CS7:
+                       lcr |= UART_LCR_WLEN7;
+                       break;
+               case CS8:
+               default:
+                       lcr |= UART_LCR_WLEN8;
+               break;
+       }
+
+       ier = readb(&ch->ch_neo_uart->ier);
+       uart_lcr = readb(&ch->ch_neo_uart->lcr);
+
+       if (baud == 0)
+               baud = 9600;
+
+       quot = ch->ch_bd->bd_dividend / baud;
+
+       if (quot != 0) {
+               ch->ch_old_baud = baud;
+               writeb(UART_LCR_DLAB, &ch->ch_neo_uart->lcr);
+               writeb((quot & 0xff), &ch->ch_neo_uart->txrx);
+               writeb((quot >> 8), &ch->ch_neo_uart->ier);
+               writeb(lcr, &ch->ch_neo_uart->lcr);
+       }
+
+       if (uart_lcr != lcr)
+               writeb(lcr, &ch->ch_neo_uart->lcr);
+
+       if (ch->ch_c_cflag & CREAD)
+               ier |= (UART_IER_RDI | UART_IER_RLSI);
+
+       ier |= (UART_IER_THRI | UART_IER_MSI);
+
+       writeb(ier, &ch->ch_neo_uart->ier);
+
+       /* Set new start/stop chars */
+       neo_set_new_start_stop_chars(ch);
+
+       if (ch->ch_c_cflag & CRTSCTS)
+               neo_set_cts_flow_control(ch);
+       else if (ch->ch_c_iflag & IXON) {
+               /* If start/stop is set to disable, then we should disable flow control */
+               if ((ch->ch_startc == __DISABLED_CHAR) || (ch->ch_stopc == __DISABLED_CHAR))
+                       neo_set_no_output_flow_control(ch);
+               else
+                       neo_set_ixon_flow_control(ch);
+       }
+       else
+               neo_set_no_output_flow_control(ch);
+
+       if (ch->ch_c_cflag & CRTSCTS)
+               neo_set_rts_flow_control(ch);
+       else if (ch->ch_c_iflag & IXOFF) {
+               /* If start/stop is set to disable, then we should disable flow control */
+               if ((ch->ch_startc == __DISABLED_CHAR) || (ch->ch_stopc == __DISABLED_CHAR))
+                       neo_set_no_input_flow_control(ch);
+               else
+                       neo_set_ixoff_flow_control(ch);
+       }
+       else
+               neo_set_no_input_flow_control(ch);
+       /*
+        * Adjust the RX FIFO Trigger level if baud is less than 9600.
+        * Not exactly elegant, but this is needed because of the Exar chip's
+        * delay on firing off the RX FIFO interrupt on slower baud rates.
+        */
+       if (baud < 9600) {
+               writeb(1, &ch->ch_neo_uart->rfifo);
+               ch->ch_r_tlevel = 1;
+       }
+
+       neo_assert_modem_signals(ch);
+
+       /* Get current status of the modem signals now */
+       neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
+       return;
+}
+
+/*
+ * jsm_neo_intr()
+ *
+ * Neo specific interrupt handler.
+ */
+static irqreturn_t neo_intr(int irq, void *voidbrd, struct pt_regs *regs)
+{
+       struct jsm_board *brd = (struct jsm_board *) voidbrd;
+       struct jsm_channel *ch;
+       int port = 0;
+       int type = 0;
+       int current_port;
+       u32 tmp;
+       u32 uart_poll;
+       unsigned long lock_flags;
+       unsigned long lock_flags2;
+       int outofloop_count = 0;
+
+       brd->intr_count++;
+
+       /* Lock out the slow poller from running on this board. */
+       spin_lock_irqsave(&brd->bd_intr_lock, lock_flags);
+
+       /*
+        * Read in "extended" IRQ information from the 32bit Neo register.
+        * Bits 0-7: What port triggered the interrupt.
+        * Bits 8-31: Each 3bits indicate what type of interrupt occurred.
+        */
+       uart_poll = readl(brd->re_map_membase + UART_17158_POLL_ADDR_OFFSET);
+
+       jsm_printk(INTR, INFO, &brd->pci_dev,
+               "%s:%d uart_poll: %x\n", __FILE__, __LINE__, uart_poll);
+
+       if (!uart_poll) {
+               jsm_printk(INTR, INFO, &brd->pci_dev,
+                       "Kernel interrupted to me, but no pending interrupts...\n");
+               spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags);
+               return IRQ_NONE;
+       }
+
+       /* At this point, we have at least SOMETHING to service, dig further... */
+
+       current_port = 0;
+
+       /* Loop on each port */
+       while (((uart_poll & 0xff) != 0) && (outofloop_count < 0xff)){
+
+               tmp = uart_poll;
+               outofloop_count++;
+
+               /* Check current port to see if it has interrupt pending */
+               if ((tmp & jsm_offset_table[current_port]) != 0) {
+                       port = current_port;
+                       type = tmp >> (8 + (port * 3));
+                       type &= 0x7;
+               } else {
+                       current_port++;
+                       continue;
+               }
+
+               jsm_printk(INTR, INFO, &brd->pci_dev,
+               "%s:%d port: %x type: %x\n", __FILE__, __LINE__, port, type);
+
+               /* Remove this port + type from uart_poll */
+               uart_poll &= ~(jsm_offset_table[port]);
+
+               if (!type) {
+                       /* If no type, just ignore it, and move onto next port */
+                       jsm_printk(INTR, ERR, &brd->pci_dev,
+                               "Interrupt with no type! port: %d\n", port);
+                       continue;
+               }
+
+               /* Switch on type of interrupt we have */
+               switch (type) {
+
+               case UART_17158_RXRDY_TIMEOUT:
+                       /*
+                        * RXRDY Time-out is cleared by reading data in the
+                       * RX FIFO until it falls below the trigger level.
+                        */
+
+                       /* Verify the port is in range. */
+                       if (port > brd->nasync)
+                               continue;
+
+                       ch = brd->channels[port];
+                       neo_copy_data_from_uart_to_queue(ch);
+
+                       /* Call our tty layer to enforce queue flow control if needed. */
+                       spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+                       jsm_check_queue_flow_control(ch);
+                       spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+
+                       continue;
+
+               case UART_17158_RX_LINE_STATUS:
+                       /*
+                        * RXRDY and RX LINE Status (logic OR of LSR[4:1])
+                        */
+                       neo_parse_lsr(brd, port);
+                       continue;
+
+               case UART_17158_TXRDY:
+                       /*
+                        * TXRDY interrupt clears after reading ISR register for the UART channel.
+                        */
+
+                       /*
+                        * Yes, this is odd...
+                        * Why would I check EVERY possibility of type of
+                        * interrupt, when we know its TXRDY???
+                        * Becuz for some reason, even tho we got triggered for TXRDY,
+                        * it seems to be occassionally wrong. Instead of TX, which
+                        * it should be, I was getting things like RXDY too. Weird.
+                        */
+                       neo_parse_isr(brd, port);
+                       continue;
+
+               case UART_17158_MSR:
+                       /*
+                        * MSR or flow control was seen.
+                        */
+                       neo_parse_isr(brd, port);
+                       continue;
+
+               default:
+                       /*
+                        * The UART triggered us with a bogus interrupt type.
+                        * It appears the Exar chip, when REALLY bogged down, will throw
+                        * these once and awhile.
+                        * Its harmless, just ignore it and move on.
+                        */
+                       jsm_printk(INTR, ERR, &brd->pci_dev,
+                               "%s:%d Unknown Interrupt type: %x\n", __FILE__, __LINE__, type);
+                       continue;
+               }
+       }
+
+       spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags);
+
+       jsm_printk(INTR, INFO, &brd->pci_dev, "finish.\n");
+       return IRQ_HANDLED;
+}
+
+/*
+ * Neo specific way of turning off the receiver.
+ * Used as a way to enforce queue flow control when in
+ * hardware flow control mode.
+ */
+static void neo_disable_receiver(struct jsm_channel *ch)
+{
+       u8 tmp = readb(&ch->ch_neo_uart->ier);
+       tmp &= ~(UART_IER_RDI);
+       writeb(tmp, &ch->ch_neo_uart->ier);
+
+       /* flush write operation */
+       neo_pci_posting_flush(ch->ch_bd);
+}
+
+
+/*
+ * Neo specific way of turning on the receiver.
+ * Used as a way to un-enforce queue flow control when in
+ * hardware flow control mode.
+ */
+static void neo_enable_receiver(struct jsm_channel *ch)
+{
+       u8 tmp = readb(&ch->ch_neo_uart->ier);
+       tmp |= (UART_IER_RDI);
+       writeb(tmp, &ch->ch_neo_uart->ier);
+
+       /* flush write operation */
+       neo_pci_posting_flush(ch->ch_bd);
+}
+
+static void neo_send_start_character(struct jsm_channel *ch)
+{
+       if (!ch)
+               return;
+
+       if (ch->ch_startc != __DISABLED_CHAR) {
+               ch->ch_xon_sends++;
+               writeb(ch->ch_startc, &ch->ch_neo_uart->txrx);
+
+               /* flush write operation */
+               neo_pci_posting_flush(ch->ch_bd);
+       }
+}
+
+static void neo_send_stop_character(struct jsm_channel *ch)
+{
+       if (!ch)
+               return;
+
+       if (ch->ch_stopc != __DISABLED_CHAR) {
+               ch->ch_xoff_sends++;
+               writeb(ch->ch_stopc, &ch->ch_neo_uart->txrx);
+
+               /* flush write operation */
+               neo_pci_posting_flush(ch->ch_bd);
+       }
+}
+
+/*
+ * neo_uart_init
+ */
+static void neo_uart_init(struct jsm_channel *ch)
+{
+       writeb(0, &ch->ch_neo_uart->ier);
+       writeb(0, &ch->ch_neo_uart->efr);
+       writeb(UART_EFR_ECB, &ch->ch_neo_uart->efr);
+
+       /* Clear out UART and FIFO */
+       readb(&ch->ch_neo_uart->txrx);
+       writeb((UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
+       readb(&ch->ch_neo_uart->lsr);
+       readb(&ch->ch_neo_uart->msr);
+
+       ch->ch_flags |= CH_FIFO_ENABLED;
+
+       /* Assert any signals we want up */
+       writeb(ch->ch_mostat, &ch->ch_neo_uart->mcr);
+}
+
+/*
+ * Make the UART completely turn off.
+ */
+static void neo_uart_off(struct jsm_channel *ch)
+{
+       /* Turn off UART enhanced bits */
+       writeb(0, &ch->ch_neo_uart->efr);
+
+       /* Stop all interrupts from occurring. */
+       writeb(0, &ch->ch_neo_uart->ier);
+}
+
+static u32 neo_get_uart_bytes_left(struct jsm_channel *ch)
+{
+       u8 left = 0;
+       u8 lsr = readb(&ch->ch_neo_uart->lsr);
+
+       /* We must cache the LSR as some of the bits get reset once read... */
+       ch->ch_cached_lsr |= lsr;
+
+       /* Determine whether the Transmitter is empty or not */
+       if (!(lsr & UART_LSR_TEMT))
+               left = 1;
+       else {
+               ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+               left = 0;
+       }
+
+       return left;
+}
+
+/* Channel lock MUST be held by the calling function! */
+static void neo_send_break(struct jsm_channel *ch)
+{
+       /*
+        * Set the time we should stop sending the break.
+        * If we are already sending a break, toss away the existing
+        * time to stop, and use this new value instead.
+        */
+
+       /* Tell the UART to start sending the break */
+       if (!(ch->ch_flags & CH_BREAK_SENDING)) {
+               u8 temp = readb(&ch->ch_neo_uart->lcr);
+               writeb((temp | UART_LCR_SBC), &ch->ch_neo_uart->lcr);
+               ch->ch_flags |= (CH_BREAK_SENDING);
+
+               /* flush write operation */
+               neo_pci_posting_flush(ch->ch_bd);
+       }
+}
+
+/*
+ * neo_send_immediate_char.
+ *
+ * Sends a specific character as soon as possible to the UART,
+ * jumping over any bytes that might be in the write queue.
+ *
+ * The channel lock MUST be held by the calling function.
+ */
+static void neo_send_immediate_char(struct jsm_channel *ch, unsigned char c)
+{
+       if (!ch)
+               return;
+
+       writeb(c, &ch->ch_neo_uart->txrx);
+
+       /* flush write operation */
+       neo_pci_posting_flush(ch->ch_bd);
+}
+
+struct board_ops jsm_neo_ops = {
+       .intr                           = neo_intr,
+       .uart_init                      = neo_uart_init,
+       .uart_off                       = neo_uart_off,
+       .param                          = neo_param,
+       .assert_modem_signals           = neo_assert_modem_signals,
+       .flush_uart_write               = neo_flush_uart_write,
+       .flush_uart_read                = neo_flush_uart_read,
+       .disable_receiver               = neo_disable_receiver,
+       .enable_receiver                = neo_enable_receiver,
+       .send_break                     = neo_send_break,
+       .clear_break                    = neo_clear_break,
+       .send_start_character           = neo_send_start_character,
+       .send_stop_character            = neo_send_stop_character,
+       .copy_data_from_queue_to_uart   = neo_copy_data_from_queue_to_uart,
+       .get_uart_bytes_left            = neo_get_uart_bytes_left,
+       .send_immediate_char            = neo_send_immediate_char
+};
diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c
new file mode 100644 (file)
index 0000000..98de225
--- /dev/null
@@ -0,0 +1,1016 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM 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, EXPRESS OR IMPLIED; 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.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_reg.h>
+#include <linux/delay.h>       /* For udelay */
+#include <linux/pci.h>
+
+#include "jsm.h"
+
+static void jsm_carrier(struct jsm_channel *ch);
+
+static inline int jsm_get_mstat(struct jsm_channel *ch)
+{
+       unsigned char mstat;
+       unsigned result;
+
+       jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "start\n");
+
+       mstat = (ch->ch_mostat | ch->ch_mistat);
+
+       result = 0;
+
+       if (mstat & UART_MCR_DTR)
+               result |= TIOCM_DTR;
+       if (mstat & UART_MCR_RTS)
+               result |= TIOCM_RTS;
+       if (mstat & UART_MSR_CTS)
+               result |= TIOCM_CTS;
+       if (mstat & UART_MSR_DSR)
+               result |= TIOCM_DSR;
+       if (mstat & UART_MSR_RI)
+               result |= TIOCM_RI;
+       if (mstat & UART_MSR_DCD)
+               result |= TIOCM_CD;
+
+       jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n");
+       return result;
+}
+
+static unsigned int jsm_tty_tx_empty(struct uart_port *port)
+{
+       return TIOCSER_TEMT;
+}
+
+/*
+ * Return modem signals to ld.
+ */
+static unsigned int jsm_tty_get_mctrl(struct uart_port *port)
+{
+       int result;
+       struct jsm_channel *channel = (struct jsm_channel *)port;
+
+       jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+       result = jsm_get_mstat(channel);
+
+       if (result < 0)
+               return -ENXIO;
+
+       jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+
+       return result;
+}
+
+/*
+ * jsm_set_modem_info()
+ *
+ * Set modem signals, called by ld.
+ */
+static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       struct jsm_channel *channel = (struct jsm_channel *)port;
+
+       jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+       if (mctrl & TIOCM_RTS)
+               channel->ch_mostat |= UART_MCR_RTS;
+       else
+               channel->ch_mostat &= ~UART_MCR_RTS;
+
+       if (mctrl & TIOCM_DTR)
+               channel->ch_mostat |= UART_MCR_DTR;
+       else
+               channel->ch_mostat &= ~UART_MCR_DTR;
+
+       channel->ch_bd->bd_ops->assert_modem_signals(channel);
+
+       jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+       udelay(10);
+}
+
+static void jsm_tty_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+       struct jsm_channel *channel = (struct jsm_channel *)port;
+
+       jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+       channel->ch_flags &= ~(CH_STOP);
+       jsm_tty_write(port);
+
+       jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+}
+
+static void jsm_tty_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+       struct jsm_channel *channel = (struct jsm_channel *)port;
+
+       jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+       channel->ch_flags |= (CH_STOP);
+
+       jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+}
+
+static void jsm_tty_send_xchar(struct uart_port *port, char ch)
+{
+       unsigned long lock_flags;
+       struct jsm_channel *channel = (struct jsm_channel *)port;
+
+       spin_lock_irqsave(&port->lock, lock_flags);
+       if (ch == port->info->tty->termios->c_cc[VSTART])
+               channel->ch_bd->bd_ops->send_start_character(channel);
+
+       if (ch == port->info->tty->termios->c_cc[VSTOP])
+               channel->ch_bd->bd_ops->send_stop_character(channel);
+       spin_unlock_irqrestore(&port->lock, lock_flags);
+}
+
+static void jsm_tty_stop_rx(struct uart_port *port)
+{
+       struct jsm_channel *channel = (struct jsm_channel *)port;
+
+       channel->ch_bd->bd_ops->disable_receiver(channel);
+}
+
+static void jsm_tty_break(struct uart_port *port, int break_state)
+{
+       unsigned long lock_flags;
+       struct jsm_channel *channel = (struct jsm_channel *)port;
+
+       spin_lock_irqsave(&port->lock, lock_flags);
+       if (break_state == -1)
+               channel->ch_bd->bd_ops->send_break(channel);
+       else
+               channel->ch_bd->bd_ops->clear_break(channel, 0);
+
+       spin_unlock_irqrestore(&port->lock, lock_flags);
+}
+
+static int jsm_tty_open(struct uart_port *port)
+{
+       struct jsm_board *brd;
+       int rc = 0;
+       struct jsm_channel *channel = (struct jsm_channel *)port;
+
+       /* Get board pointer from our array of majors we have allocated */
+       brd = channel->ch_bd;
+
+       /*
+        * Allocate channel buffers for read/write/error.
+        * Set flag, so we don't get trounced on.
+        */
+       channel->ch_flags |= (CH_OPENING);
+
+       /* Drop locks, as malloc with GFP_KERNEL can sleep */
+
+       if (!channel->ch_rqueue) {
+               channel->ch_rqueue = (u8 *) kmalloc(RQUEUESIZE, GFP_KERNEL);
+               if (!channel->ch_rqueue) {
+                       jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
+                               "unable to allocate read queue buf");
+                       return -ENOMEM;
+               }
+               memset(channel->ch_rqueue, 0, RQUEUESIZE);
+       }
+       if (!channel->ch_equeue) {
+               channel->ch_equeue = (u8 *) kmalloc(EQUEUESIZE, GFP_KERNEL);
+               if (!channel->ch_equeue) {
+                       jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
+                               "unable to allocate error queue buf");
+                       return -ENOMEM;
+               }
+               memset(channel->ch_equeue, 0, EQUEUESIZE);
+       }
+       if (!channel->ch_wqueue) {
+               channel->ch_wqueue = (u8 *) kmalloc(WQUEUESIZE, GFP_KERNEL);
+               if (!channel->ch_wqueue) {
+                       jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
+                               "unable to allocate write queue buf");
+                       return -ENOMEM;
+               }
+               memset(channel->ch_wqueue, 0, WQUEUESIZE);
+       }
+
+       channel->ch_flags &= ~(CH_OPENING);
+       /*
+        * Initialize if neither terminal is open.
+        */
+       jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev,
+               "jsm_open: initializing channel in open...\n");
+
+       /*
+        * Flush input queues.
+        */
+       channel->ch_r_head = channel->ch_r_tail = 0;
+       channel->ch_e_head = channel->ch_e_tail = 0;
+       channel->ch_w_head = channel->ch_w_tail = 0;
+
+       brd->bd_ops->flush_uart_write(channel);
+       brd->bd_ops->flush_uart_read(channel);
+
+       channel->ch_flags = 0;
+       channel->ch_cached_lsr = 0;
+       channel->ch_stops_sent = 0;
+
+       channel->ch_c_cflag     = port->info->tty->termios->c_cflag;
+       channel->ch_c_iflag     = port->info->tty->termios->c_iflag;
+       channel->ch_c_oflag     = port->info->tty->termios->c_oflag;
+       channel->ch_c_lflag     = port->info->tty->termios->c_lflag;
+       channel->ch_startc = port->info->tty->termios->c_cc[VSTART];
+       channel->ch_stopc = port->info->tty->termios->c_cc[VSTOP];
+
+       /* Tell UART to init itself */
+       brd->bd_ops->uart_init(channel);
+
+       /*
+        * Run param in case we changed anything
+        */
+       brd->bd_ops->param(channel);
+
+       jsm_carrier(channel);
+
+       channel->ch_open_count++;
+
+       jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev, "finish\n");
+       return rc;
+}
+
+static void jsm_tty_close(struct uart_port *port)
+{
+       struct jsm_board *bd;
+       struct termios *ts;
+       struct jsm_channel *channel = (struct jsm_channel *)port;
+
+       jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+       bd = channel->ch_bd;
+       ts = channel->uart_port.info->tty->termios;
+
+       channel->ch_flags &= ~(CH_STOPI);
+
+       channel->ch_open_count--;
+
+       /*
+        * If we have HUPCL set, lower DTR and RTS
+        */
+       if (channel->ch_c_cflag & HUPCL) {
+               jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev,
+                       "Close. HUPCL set, dropping DTR/RTS\n");
+
+               /* Drop RTS/DTR */
+               channel->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS);
+               bd->bd_ops->assert_modem_signals(channel);
+       }
+
+       channel->ch_old_baud = 0;
+
+       /* Turn off UART interrupts for this port */
+       channel->ch_bd->bd_ops->uart_off(channel);
+
+       jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "finish\n");
+}
+
+static void jsm_tty_set_termios(struct uart_port *port,
+                                struct termios *termios,
+                                struct termios *old_termios)
+{
+       unsigned long lock_flags;
+       struct jsm_channel *channel = (struct jsm_channel *)port;
+
+       spin_lock_irqsave(&port->lock, lock_flags);
+       channel->ch_c_cflag     = termios->c_cflag;
+       channel->ch_c_iflag     = termios->c_iflag;
+       channel->ch_c_oflag     = termios->c_oflag;
+       channel->ch_c_lflag     = termios->c_lflag;
+       channel->ch_startc      = termios->c_cc[VSTART];
+       channel->ch_stopc       = termios->c_cc[VSTOP];
+
+       channel->ch_bd->bd_ops->param(channel);
+       jsm_carrier(channel);
+       spin_unlock_irqrestore(&port->lock, lock_flags);
+}
+
+static const char *jsm_tty_type(struct uart_port *port)
+{
+       return "jsm";
+}
+
+static void jsm_tty_release_port(struct uart_port *port)
+{
+}
+
+static int jsm_tty_request_port(struct uart_port *port)
+{
+       return 0;
+}
+
+static void jsm_config_port(struct uart_port *port, int flags)
+{
+       port->type = PORT_JSM;
+}
+
+static struct uart_ops jsm_ops = {
+       .tx_empty       = jsm_tty_tx_empty,
+       .set_mctrl      = jsm_tty_set_mctrl,
+       .get_mctrl      = jsm_tty_get_mctrl,
+       .stop_tx        = jsm_tty_stop_tx,
+       .start_tx       = jsm_tty_start_tx,
+       .send_xchar     = jsm_tty_send_xchar,
+       .stop_rx        = jsm_tty_stop_rx,
+       .break_ctl      = jsm_tty_break,
+       .startup        = jsm_tty_open,
+       .shutdown       = jsm_tty_close,
+       .set_termios    = jsm_tty_set_termios,
+       .type           = jsm_tty_type,
+       .release_port   = jsm_tty_release_port,
+       .request_port   = jsm_tty_request_port,
+       .config_port    = jsm_config_port,
+};
+
+/*
+ * jsm_tty_init()
+ *
+ * Init the tty subsystem.  Called once per board after board has been
+ * downloaded and init'ed.
+ */
+int jsm_tty_init(struct jsm_board *brd)
+{
+       int i;
+       void __iomem *vaddr;
+       struct jsm_channel *ch;
+
+       if (!brd)
+               return -ENXIO;
+
+       jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+
+       /*
+        * Initialize board structure elements.
+        */
+
+       brd->nasync = brd->maxports;
+
+       /*
+        * Allocate channel memory that might not have been allocated
+        * when the driver was first loaded.
+        */
+       for (i = 0; i < brd->nasync; i++) {
+               if (!brd->channels[i]) {
+
+                       /*
+                        * Okay to malloc with GFP_KERNEL, we are not at
+                        * interrupt context, and there are no locks held.
+                        */
+                       brd->channels[i] = kmalloc(sizeof(struct jsm_channel), GFP_KERNEL);
+                       if (!brd->channels[i]) {
+                               jsm_printk(CORE, ERR, &brd->pci_dev,
+                                       "%s:%d Unable to allocate memory for channel struct\n",
+                                                        __FILE__, __LINE__);
+                       }
+                       memset(brd->channels[i], 0, sizeof(struct jsm_channel));
+               }
+       }
+
+       ch = brd->channels[0];
+       vaddr = brd->re_map_membase;
+
+       /* Set up channel variables */
+       for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {
+
+               if (!brd->channels[i])
+                       continue;
+
+               spin_lock_init(&ch->ch_lock);
+
+               if (brd->bd_uart_offset == 0x200)
+                       ch->ch_neo_uart =  vaddr + (brd->bd_uart_offset * i);
+
+               ch->ch_bd = brd;
+               ch->ch_portnum = i;
+
+               /* .25 second delay */
+               ch->ch_close_delay = 250;
+
+               init_waitqueue_head(&ch->ch_flags_wait);
+       }
+
+       jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+       return 0;
+}
+
+int jsm_uart_port_init(struct jsm_board *brd)
+{
+       int i;
+       struct jsm_channel *ch;
+
+       if (!brd)
+               return -ENXIO;
+
+       jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+
+       /*
+        * Initialize board structure elements.
+        */
+
+       brd->nasync = brd->maxports;
+
+       /* Set up channel variables */
+       for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {
+
+               if (!brd->channels[i])
+                       continue;
+
+               brd->channels[i]->uart_port.irq = brd->irq;
+               brd->channels[i]->uart_port.type = PORT_JSM;
+               brd->channels[i]->uart_port.iotype = UPIO_MEM;
+               brd->channels[i]->uart_port.membase = brd->re_map_membase;
+               brd->channels[i]->uart_port.fifosize = 16;
+               brd->channels[i]->uart_port.ops = &jsm_ops;
+               brd->channels[i]->uart_port.line = brd->channels[i]->ch_portnum + brd->boardnum * 2;
+               if (uart_add_one_port (&jsm_uart_driver, &brd->channels[i]->uart_port))
+                       printk(KERN_INFO "Added device failed\n");
+               else
+                       printk(KERN_INFO "Added device \n");
+       }
+
+       jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+       return 0;
+}
+
+int jsm_remove_uart_port(struct jsm_board *brd)
+{
+       int i;
+       struct jsm_channel *ch;
+
+       if (!brd)
+               return -ENXIO;
+
+       jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+
+       /*
+        * Initialize board structure elements.
+        */
+
+       brd->nasync = brd->maxports;
+
+       /* Set up channel variables */
+       for (i = 0; i < brd->nasync; i++) {
+
+               if (!brd->channels[i])
+                       continue;
+
+               ch = brd->channels[i];
+
+               uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port);
+       }
+
+       jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+       return 0;
+}
+
+void jsm_input(struct jsm_channel *ch)
+{
+       struct jsm_board *bd;
+       struct tty_struct *tp;
+       u32 rmask;
+       u16 head;
+       u16 tail;
+       int data_len;
+       unsigned long lock_flags;
+       int flip_len;
+       int len = 0;
+       int n = 0;
+       char *buf = NULL;
+       char *buf2 = NULL;
+       int s = 0;
+       int i = 0;
+
+       jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n");
+
+       if (!ch)
+               return;
+
+       tp = ch->uart_port.info->tty;
+
+       bd = ch->ch_bd;
+       if(!bd)
+               return;
+
+       spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+       /*
+        *Figure the number of characters in the buffer.
+        *Exit immediately if none.
+        */
+
+       rmask = RQUEUEMASK;
+
+       head = ch->ch_r_head & rmask;
+       tail = ch->ch_r_tail & rmask;
+
+       data_len = (head - tail) & rmask;
+       if (data_len == 0) {
+               spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+               return;
+       }
+
+       jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n");
+
+       /*
+        *If the device is not open, or CREAD is off, flush
+        *input data and return immediately.
+        */
+       if (!tp ||
+               !(tp->termios->c_cflag & CREAD) ) {
+
+               jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+                       "input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum);
+               ch->ch_r_head = tail;
+
+               /* Force queue flow control to be released, if needed */
+               jsm_check_queue_flow_control(ch);
+
+               spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+               return;
+       }
+
+       /*
+        * If we are throttled, simply don't read any data.
+        */
+       if (ch->ch_flags & CH_STOPI) {
+               spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+               jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+                       "Port %d throttled, not reading any data. head: %x tail: %x\n",
+                       ch->ch_portnum, head, tail);
+               return;
+       }
+
+       jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start 2\n");
+
+       /*
+        * If the rxbuf is empty and we are not throttled, put as much
+        * as we can directly into the linux TTY flip buffer.
+        * The jsm_rawreadok case takes advantage of carnal knowledge that
+        * the char_buf and the flag_buf are next to each other and
+        * are each of (2 * TTY_FLIPBUF_SIZE) size.
+        *
+        * NOTE: if(!tty->real_raw), the call to ldisc.receive_buf
+        *actually still uses the flag buffer, so you can't
+        *use it for input data
+        */
+       if (jsm_rawreadok) {
+               if (tp->real_raw)
+                       flip_len = MYFLIPLEN;
+               else
+                       flip_len = 2 * TTY_FLIPBUF_SIZE;
+       } else
+               flip_len = TTY_FLIPBUF_SIZE - tp->flip.count;
+
+       len = min(data_len, flip_len);
+       len = min(len, (N_TTY_BUF_SIZE - 1) - tp->read_cnt);
+
+       if (len <= 0) {
+               spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+               jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "jsm_input 1\n");
+               return;
+       }
+
+       /*
+        * If we're bypassing flip buffers on rx, we can blast it
+        * right into the beginning of the buffer.
+        */
+       if (jsm_rawreadok) {
+               if (tp->real_raw) {
+                       if (ch->ch_flags & CH_FLIPBUF_IN_USE) {
+                               jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+                                       "JSM - FLIPBUF in use. delaying input\n");
+                               spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+                               return;
+                       }
+                       ch->ch_flags |= CH_FLIPBUF_IN_USE;
+                       buf = ch->ch_bd->flipbuf;
+                       buf2 = NULL;
+               } else {
+                       buf = tp->flip.char_buf;
+                       buf2 = tp->flip.flag_buf;
+               }
+       } else {
+               buf = tp->flip.char_buf_ptr;
+               buf2 = tp->flip.flag_buf_ptr;
+       }
+
+       n = len;
+
+       /*
+        * n now contains the most amount of data we can copy,
+        * bounded either by the flip buffer size or the amount
+        * of data the card actually has pending...
+        */
+       while (n) {
+               s = ((head >= tail) ? head : RQUEUESIZE) - tail;
+               s = min(s, n);
+
+               if (s <= 0)
+                       break;
+
+               memcpy(buf, ch->ch_rqueue + tail, s);
+
+               /* buf2 is only set when port isn't raw */
+               if (buf2)
+                       memcpy(buf2, ch->ch_equeue + tail, s);
+
+               tail += s;
+               buf += s;
+               if (buf2)
+                       buf2 += s;
+               n -= s;
+               /* Flip queue if needed */
+               tail &= rmask;
+       }
+
+       /*
+        * In high performance mode, we don't have to update
+        * flag_buf or any of the counts or pointers into flip buf.
+        */
+       if (!jsm_rawreadok) {
+               if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) {
+                       for (i = 0; i < len; i++) {
+                               /*
+                                * Give the Linux ld the flags in the
+                                * format it likes.
+                                */
+                               if (tp->flip.flag_buf_ptr[i] & UART_LSR_BI)
+                                       tp->flip.flag_buf_ptr[i] = TTY_BREAK;
+                               else if (tp->flip.flag_buf_ptr[i] & UART_LSR_PE)
+                                       tp->flip.flag_buf_ptr[i] = TTY_PARITY;
+                               else if (tp->flip.flag_buf_ptr[i] & UART_LSR_FE)
+                                       tp->flip.flag_buf_ptr[i] = TTY_FRAME;
+                               else
+                                       tp->flip.flag_buf_ptr[i] = TTY_NORMAL;
+                       }
+               } else {
+                       memset(tp->flip.flag_buf_ptr, 0, len);
+               }
+
+               tp->flip.char_buf_ptr += len;
+               tp->flip.flag_buf_ptr += len;
+               tp->flip.count += len;
+       }
+       else if (!tp->real_raw) {
+               if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) {
+                       for (i = 0; i < len; i++) {
+                               /*
+                                * Give the Linux ld the flags in the
+                                * format it likes.
+                                */
+                               if (tp->flip.flag_buf_ptr[i] & UART_LSR_BI)
+                                       tp->flip.flag_buf_ptr[i] = TTY_BREAK;
+                               else if (tp->flip.flag_buf_ptr[i] & UART_LSR_PE)
+                                       tp->flip.flag_buf_ptr[i] = TTY_PARITY;
+                               else if (tp->flip.flag_buf_ptr[i] & UART_LSR_FE)
+                                       tp->flip.flag_buf_ptr[i] = TTY_FRAME;
+                               else
+                                       tp->flip.flag_buf_ptr[i] = TTY_NORMAL;
+                       }
+               } else
+                       memset(tp->flip.flag_buf, 0, len);
+       }
+
+       /*
+        * If we're doing raw reads, jam it right into the
+        * line disc bypassing the flip buffers.
+        */
+       if (jsm_rawreadok) {
+               if (tp->real_raw) {
+                       ch->ch_r_tail = tail & rmask;
+                       ch->ch_e_tail = tail & rmask;
+
+                       jsm_check_queue_flow_control(ch);
+
+                       /* !!! WE *MUST* LET GO OF ALL LOCKS BEFORE CALLING RECEIVE BUF !!! */
+
+                       spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+                       jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+                               "jsm_input. %d real_raw len:%d calling receive_buf for board %d\n",
+                               __LINE__, len, ch->ch_bd->boardnum);
+                       tp->ldisc.receive_buf(tp, ch->ch_bd->flipbuf, NULL, len);
+
+                       /* Allow use of channel flip buffer again */
+                       spin_lock_irqsave(&ch->ch_lock, lock_flags);
+                       ch->ch_flags &= ~CH_FLIPBUF_IN_USE;
+                       spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+               } else {
+                       ch->ch_r_tail = tail & rmask;
+                       ch->ch_e_tail = tail & rmask;
+
+                       jsm_check_queue_flow_control(ch);
+
+                       /* !!! WE *MUST* LET GO OF ALL LOCKS BEFORE CALLING RECEIVE BUF !!! */
+                       spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+                       jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+                               "jsm_input. %d not real_raw len:%d calling receive_buf for board %d\n",
+                               __LINE__, len, ch->ch_bd->boardnum);
+
+                       tp->ldisc.receive_buf(tp, tp->flip.char_buf, tp->flip.flag_buf, len);
+               }
+       } else {
+               ch->ch_r_tail = tail & rmask;
+               ch->ch_e_tail = tail & rmask;
+
+               jsm_check_queue_flow_control(ch);
+
+               spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+               jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+                       "jsm_input. %d not jsm_read raw okay scheduling flip\n", __LINE__);
+               tty_schedule_flip(tp);
+       }
+
+       jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n");
+}
+
+static void jsm_carrier(struct jsm_channel *ch)
+{
+       struct jsm_board *bd;
+
+       int virt_carrier = 0;
+       int phys_carrier = 0;
+
+       jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, "start\n");
+       if (!ch)
+               return;
+
+       bd = ch->ch_bd;
+
+       if (!bd)
+               return;
+
+       if (ch->ch_mistat & UART_MSR_DCD) {
+               jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
+                       "mistat: %x D_CD: %x\n", ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD);
+               phys_carrier = 1;
+       }
+
+       if (ch->ch_c_cflag & CLOCAL)
+               virt_carrier = 1;
+
+       jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
+               "DCD: physical: %d virt: %d\n", phys_carrier, virt_carrier);
+
+       /*
+        * Test for a VIRTUAL carrier transition to HIGH.
+        */
+       if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) {
+
+               /*
+                * When carrier rises, wake any threads waiting
+                * for carrier in the open routine.
+                */
+
+               jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
+                       "carrier: virt DCD rose\n");
+
+               if (waitqueue_active(&(ch->ch_flags_wait)))
+                       wake_up_interruptible(&ch->ch_flags_wait);
+       }
+
+       /*
+        * Test for a PHYSICAL carrier transition to HIGH.
+        */
+       if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) {
+
+               /*
+                * When carrier rises, wake any threads waiting
+                * for carrier in the open routine.
+                */
+
+               jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
+                       "carrier: physical DCD rose\n");
+
+               if (waitqueue_active(&(ch->ch_flags_wait)))
+                       wake_up_interruptible(&ch->ch_flags_wait);
+       }
+
+       /*
+        *  Test for a PHYSICAL transition to low, so long as we aren't
+        *  currently ignoring physical transitions (which is what "virtual
+        *  carrier" indicates).
+        *
+        *  The transition of the virtual carrier to low really doesn't
+        *  matter... it really only means "ignore carrier state", not
+        *  "make pretend that carrier is there".
+        */
+       if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0)
+                       && (phys_carrier == 0)) {
+               /*
+                *      When carrier drops:
+                *
+                *      Drop carrier on all open units.
+                *
+                *      Flush queues, waking up any task waiting in the
+                *      line discipline.
+                *
+                *      Send a hangup to the control terminal.
+                *
+                *      Enable all select calls.
+                */
+               if (waitqueue_active(&(ch->ch_flags_wait)))
+                       wake_up_interruptible(&ch->ch_flags_wait);
+       }
+
+       /*
+        *  Make sure that our cached values reflect the current reality.
+        */
+       if (virt_carrier == 1)
+               ch->ch_flags |= CH_FCAR;
+       else
+               ch->ch_flags &= ~CH_FCAR;
+
+       if (phys_carrier == 1)
+               ch->ch_flags |= CH_CD;
+       else
+               ch->ch_flags &= ~CH_CD;
+}
+
+
+void jsm_check_queue_flow_control(struct jsm_channel *ch)
+{
+       int qleft = 0;
+
+       /* Store how much space we have left in the queue */
+       if ((qleft = ch->ch_r_tail - ch->ch_r_head - 1) < 0)
+               qleft += RQUEUEMASK + 1;
+
+       /*
+        * Check to see if we should enforce flow control on our queue because
+        * the ld (or user) isn't reading data out of our queue fast enuf.
+        *
+        * NOTE: This is done based on what the current flow control of the
+        * port is set for.
+        *
+        * 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt.
+        *      This will cause the UART's FIFO to back up, and force
+        *      the RTS signal to be dropped.
+        * 2) SWFLOW (IXOFF) - Keep trying to send a stop character to
+        *      the other side, in hopes it will stop sending data to us.
+        * 3) NONE - Nothing we can do.  We will simply drop any extra data
+        *      that gets sent into us when the queue fills up.
+        */
+       if (qleft < 256) {
+               /* HWFLOW */
+               if (ch->ch_c_cflag & CRTSCTS) {
+                       if(!(ch->ch_flags & CH_RECEIVER_OFF)) {
+                               ch->ch_bd->bd_ops->disable_receiver(ch);
+                               ch->ch_flags |= (CH_RECEIVER_OFF);
+                               jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+                                       "Internal queue hit hilevel mark (%d)! Turning off interrupts.\n",
+                                       qleft);
+                       }
+               }
+               /* SWFLOW */
+               else if (ch->ch_c_iflag & IXOFF) {
+                       if (ch->ch_stops_sent <= MAX_STOPS_SENT) {
+                               ch->ch_bd->bd_ops->send_stop_character(ch);
+                               ch->ch_stops_sent++;
+                               jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+                                       "Sending stop char! Times sent: %x\n", ch->ch_stops_sent);
+                       }
+               }
+       }
+
+       /*
+        * Check to see if we should unenforce flow control because
+        * ld (or user) finally read enuf data out of our queue.
+        *
+        * NOTE: This is done based on what the current flow control of the
+        * port is set for.
+        *
+        * 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt.
+        *      This will cause the UART's FIFO to raise RTS back up,
+        *      which will allow the other side to start sending data again.
+        * 2) SWFLOW (IXOFF) - Send a start character to
+        *      the other side, so it will start sending data to us again.
+        * 3) NONE - Do nothing. Since we didn't do anything to turn off the
+        *      other side, we don't need to do anything now.
+        */
+       if (qleft > (RQUEUESIZE / 2)) {
+               /* HWFLOW */
+               if (ch->ch_c_cflag & CRTSCTS) {
+                       if (ch->ch_flags & CH_RECEIVER_OFF) {
+                               ch->ch_bd->bd_ops->enable_receiver(ch);
+                               ch->ch_flags &= ~(CH_RECEIVER_OFF);
+                               jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+                                       "Internal queue hit lowlevel mark (%d)! Turning on interrupts.\n",
+                                       qleft);
+                       }
+               }
+               /* SWFLOW */
+               else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) {
+                       ch->ch_stops_sent = 0;
+                       ch->ch_bd->bd_ops->send_start_character(ch);
+                       jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Sending start char!\n");
+               }
+       }
+}
+
+/*
+ * jsm_tty_write()
+ *
+ * Take data from the user or kernel and send it out to the FEP.
+ * In here exists all the Transparent Print magic as well.
+ */
+int jsm_tty_write(struct uart_port *port)
+{
+       int bufcount = 0, n = 0;
+       int data_count = 0,data_count1 =0;
+       u16 head;
+       u16 tail;
+       u16 tmask;
+       u32 remain;
+       int temp_tail = port->info->xmit.tail;
+       struct jsm_channel *channel = (struct jsm_channel *)port;
+
+       tmask = WQUEUEMASK;
+       head = (channel->ch_w_head) & tmask;
+       tail = (channel->ch_w_tail) & tmask;
+
+       if ((bufcount = tail - head - 1) < 0)
+               bufcount += WQUEUESIZE;
+
+       n = bufcount;
+
+       n = min(n, 56);
+       remain = WQUEUESIZE - head;
+
+       data_count = 0;
+       if (n >= remain) {
+               n -= remain;
+               while ((port->info->xmit.head != temp_tail) &&
+               (data_count < remain)) {
+                       channel->ch_wqueue[head++] =
+                       port->info->xmit.buf[temp_tail];
+
+                       temp_tail++;
+                       temp_tail &= (UART_XMIT_SIZE - 1);
+                       data_count++;
+               }
+               if (data_count == remain) head = 0;
+       }
+
+       data_count1 = 0;
+       if (n > 0) {
+               remain = n;
+               while ((port->info->xmit.head != temp_tail) &&
+                       (data_count1 < remain)) {
+                       channel->ch_wqueue[head++] =
+                               port->info->xmit.buf[temp_tail];
+
+                       temp_tail++;
+                       temp_tail &= (UART_XMIT_SIZE - 1);
+                       data_count1++;
+
+               }
+       }
+
+       port->info->xmit.tail = temp_tail;
+
+       data_count += data_count1;
+       if (data_count) {
+               head &= tmask;
+               channel->ch_w_head = head;
+       }
+
+       if (data_count) {
+               channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel);
+       }
+
+       return data_count;
+}
diff --git a/drivers/serial/vr41xx_siu.c b/drivers/serial/vr41xx_siu.c
new file mode 100644 (file)
index 0000000..1f98532
--- /dev/null
@@ -0,0 +1,1050 @@
+/*
+ *  Driver for NEC VR4100 series Serial Interface Unit.
+ *
+ *  Copyright (C) 2004-2005  Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
+ *
+ *  Based on drivers/serial/8250.c, by 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.
+ *
+ *  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_VR41XX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/console.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include <asm/io.h>
+#include <asm/vr41xx/siu.h>
+#include <asm/vr41xx/vr41xx.h>
+
+#define SIU_PORTS_MAX  2
+#define SIU_BAUD_BASE  1152000
+#define SIU_MAJOR      204
+#define SIU_MINOR_BASE 82
+
+#define RX_MAX_COUNT   256
+#define TX_MAX_COUNT   15
+
+#define SIUIRSEL       0x08
+ #define TMICMODE      0x20
+ #define TMICTX                0x10
+ #define IRMSEL                0x0c
+ #define IRMSEL_HP     0x08
+ #define IRMSEL_TEMIC  0x04
+ #define IRMSEL_SHARP  0x00
+ #define IRUSESEL      0x02
+ #define SIRSEL                0x01
+
+struct siu_port {
+       unsigned int type;
+       unsigned int irq;
+       unsigned long start;
+};
+
+static const struct siu_port siu_type1_ports[] = {
+       {       .type           = PORT_VR41XX_SIU,
+               .irq            = SIU_IRQ,
+               .start          = 0x0c000000UL,         },
+};
+
+#define SIU_TYPE1_NR_PORTS     (sizeof(siu_type1_ports) / sizeof(struct siu_port))
+
+static const struct siu_port siu_type2_ports[] = {
+       {       .type           = PORT_VR41XX_SIU,
+               .irq            = SIU_IRQ,
+               .start          = 0x0f000800UL,         },
+       {       .type           = PORT_VR41XX_DSIU,
+               .irq            = DSIU_IRQ,
+               .start          = 0x0f000820UL,         },
+};
+
+#define SIU_TYPE2_NR_PORTS     (sizeof(siu_type2_ports) / sizeof(struct siu_port))
+
+static struct uart_port siu_uart_ports[SIU_PORTS_MAX];
+static uint8_t lsr_break_flag[SIU_PORTS_MAX];
+
+#define siu_read(port, offset)         readb((port)->membase + (offset))
+#define siu_write(port, offset, value) writeb((value), (port)->membase + (offset))
+
+void vr41xx_select_siu_interface(siu_interface_t interface)
+{
+       struct uart_port *port;
+       unsigned long flags;
+       uint8_t irsel;
+
+       port = &siu_uart_ports[0];
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       irsel = siu_read(port, SIUIRSEL);
+       if (interface == SIU_INTERFACE_IRDA)
+               irsel |= SIRSEL;
+       else
+               irsel &= ~SIRSEL;
+       siu_write(port, SIUIRSEL, irsel);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(vr41xx_select_siu_interface);
+
+void vr41xx_use_irda(irda_use_t use)
+{
+       struct uart_port *port;
+       unsigned long flags;
+       uint8_t irsel;
+
+       port = &siu_uart_ports[0];
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       irsel = siu_read(port, SIUIRSEL);
+       if (use == FIR_USE_IRDA)
+               irsel |= IRUSESEL;
+       else
+               irsel &= ~IRUSESEL;
+       siu_write(port, SIUIRSEL, irsel);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(vr41xx_use_irda);
+
+void vr41xx_select_irda_module(irda_module_t module, irda_speed_t speed)
+{
+       struct uart_port *port;
+       unsigned long flags;
+       uint8_t irsel;
+
+       port = &siu_uart_ports[0];
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       irsel = siu_read(port, SIUIRSEL);
+       irsel &= ~(IRMSEL | TMICTX | TMICMODE);
+       switch (module) {
+       case SHARP_IRDA:
+               irsel |= IRMSEL_SHARP;
+               break;
+       case TEMIC_IRDA:
+               irsel |= IRMSEL_TEMIC | TMICMODE;
+               if (speed == IRDA_TX_4MBPS)
+                       irsel |= TMICTX;
+               break;
+       case HP_IRDA:
+               irsel |= IRMSEL_HP;
+               break;
+       default:
+               break;
+       }
+       siu_write(port, SIUIRSEL, irsel);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(vr41xx_select_irda_module);
+
+static inline void siu_clear_fifo(struct uart_port *port)
+{
+       siu_write(port, UART_FCR, UART_FCR_ENABLE_FIFO);
+       siu_write(port, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
+                                 UART_FCR_CLEAR_XMIT);
+       siu_write(port, UART_FCR, 0);
+}
+
+static inline int siu_probe_ports(void)
+{
+       switch (current_cpu_data.cputype) {
+       case CPU_VR4111:
+       case CPU_VR4121:
+               return SIU_TYPE1_NR_PORTS;
+       case CPU_VR4122:
+       case CPU_VR4131:
+       case CPU_VR4133:
+               return SIU_TYPE2_NR_PORTS;
+       }
+
+       return 0;
+}
+
+static inline unsigned long siu_port_size(struct uart_port *port)
+{
+       switch (port->type) {
+       case PORT_VR41XX_SIU:
+               return 11UL;
+       case PORT_VR41XX_DSIU:
+               return 8UL;
+       }
+
+       return 0;
+}
+
+static inline unsigned int siu_check_type(struct uart_port *port)
+{
+       switch (current_cpu_data.cputype) {
+       case CPU_VR4111:
+       case CPU_VR4121:
+               if (port->line == 0)
+                       return PORT_VR41XX_SIU;
+               break;
+       case CPU_VR4122:
+       case CPU_VR4131:
+       case CPU_VR4133:
+               if (port->line == 0)
+                       return PORT_VR41XX_SIU;
+               else if (port->line == 1)
+                       return PORT_VR41XX_DSIU;
+               break;
+       }
+
+       return PORT_UNKNOWN;
+}
+
+static inline const char *siu_type_name(struct uart_port *port)
+{
+       switch (port->type) {
+       case PORT_VR41XX_SIU:
+               return "SIU";
+       case PORT_VR41XX_DSIU:
+               return "DSIU";
+       }
+
+       return NULL;
+}
+
+static unsigned int siu_tx_empty(struct uart_port *port)
+{
+       uint8_t lsr;
+
+       lsr = siu_read(port, UART_LSR);
+       if (lsr & UART_LSR_TEMT)
+               return TIOCSER_TEMT;
+
+       return 0;
+}
+
+static void siu_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       uint8_t mcr = 0;
+
+       if (mctrl & TIOCM_DTR)
+               mcr |= UART_MCR_DTR;
+       if (mctrl & TIOCM_RTS)
+               mcr |= UART_MCR_RTS;
+       if (mctrl & TIOCM_OUT1)
+               mcr |= UART_MCR_OUT1;
+       if (mctrl & TIOCM_OUT2)
+               mcr |= UART_MCR_OUT2;
+       if (mctrl & TIOCM_LOOP)
+               mcr |= UART_MCR_LOOP;
+
+       siu_write(port, UART_MCR, mcr);
+}
+
+static unsigned int siu_get_mctrl(struct uart_port *port)
+{
+       uint8_t msr;
+       unsigned int mctrl = 0;
+
+       msr = siu_read(port, UART_MSR);
+       if (msr & UART_MSR_DCD)
+               mctrl |= TIOCM_CAR;
+       if (msr & UART_MSR_RI)
+               mctrl |= TIOCM_RNG;
+       if (msr & UART_MSR_DSR)
+               mctrl |= TIOCM_DSR;
+       if (msr & UART_MSR_CTS)
+               mctrl |= TIOCM_CTS;
+
+       return mctrl;
+}
+
+static void siu_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+       unsigned long flags;
+       uint8_t ier;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       ier = siu_read(port, UART_IER);
+       ier &= ~UART_IER_THRI;
+       siu_write(port, UART_IER, ier);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void siu_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+       unsigned long flags;
+       uint8_t ier;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       ier = siu_read(port, UART_IER);
+       ier |= UART_IER_THRI;
+       siu_write(port, UART_IER, ier);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void siu_stop_rx(struct uart_port *port)
+{
+       unsigned long flags;
+       uint8_t ier;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       ier = siu_read(port, UART_IER);
+       ier &= ~UART_IER_RLSI;
+       siu_write(port, UART_IER, ier);
+
+       port->read_status_mask &= ~UART_LSR_DR;
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void siu_enable_ms(struct uart_port *port)
+{
+       unsigned long flags;
+       uint8_t ier;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       ier = siu_read(port, UART_IER);
+       ier |= UART_IER_MSI;
+       siu_write(port, UART_IER, ier);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void siu_break_ctl(struct uart_port *port, int ctl)
+{
+       unsigned long flags;
+       uint8_t lcr;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       lcr = siu_read(port, UART_LCR);
+       if (ctl == -1)
+               lcr |= UART_LCR_SBC;
+       else
+               lcr &= ~UART_LCR_SBC;
+       siu_write(port, UART_LCR, lcr);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static inline void receive_chars(struct uart_port *port, uint8_t *status,
+                                 struct pt_regs *regs)
+{
+       struct tty_struct *tty;
+       uint8_t lsr, ch;
+       char flag;
+       int max_count = RX_MAX_COUNT;
+
+       tty = port->info->tty;
+       lsr = *status;
+
+       do {
+               if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
+                       if (tty->low_latency)
+                               tty_flip_buffer_push(tty);
+               }
+
+               ch = siu_read(port, UART_RX);
+               port->icount.rx++;
+               flag = TTY_NORMAL;
+
+#ifdef CONFIG_SERIAL_VR41XX_CONSOLE
+               lsr |= lsr_break_flag[port->line];
+               lsr_break_flag[port->line] = 0;
+#endif
+               if (unlikely(lsr & (UART_LSR_BI | UART_LSR_FE |
+                                   UART_LSR_PE | UART_LSR_OE))) {
+                       if (lsr & UART_LSR_BI) {
+                               lsr &= ~(UART_LSR_FE | UART_LSR_PE);
+                               port->icount.brk++;
+
+                               if (uart_handle_break(port))
+                                       goto ignore_char;
+                       }
+
+                       if (lsr & UART_LSR_FE)
+                               port->icount.frame++;
+                       if (lsr & UART_LSR_PE)
+                               port->icount.parity++;
+                       if (lsr & UART_LSR_OE)
+                               port->icount.overrun++;
+
+                       lsr &= port->read_status_mask;
+                       if (lsr & UART_LSR_BI)
+                               flag = TTY_BREAK;
+                       if (lsr & UART_LSR_FE)
+                               flag = TTY_FRAME;
+                       if (lsr & UART_LSR_PE)
+                               flag = TTY_PARITY;
+               }
+
+               if (uart_handle_sysrq_char(port, ch, regs))
+                       goto ignore_char;
+
+               uart_insert_char(port, lsr, UART_LSR_OE, ch, flag);
+
+       ignore_char:
+               lsr = siu_read(port, UART_LSR);
+       } while ((lsr & UART_LSR_DR) && (max_count-- > 0));
+
+       tty_flip_buffer_push(tty);
+
+       *status = lsr;
+}
+
+static inline void check_modem_status(struct uart_port *port)
+{
+       uint8_t msr;
+
+       msr = siu_read(port, UART_MSR);
+       if ((msr & UART_MSR_ANY_DELTA) == 0)
+               return;
+       if (msr & UART_MSR_DDCD)
+               uart_handle_dcd_change(port, msr & UART_MSR_DCD);
+       if (msr & UART_MSR_TERI)
+               port->icount.rng++;
+       if (msr & UART_MSR_DDSR)
+               port->icount.dsr++;
+       if (msr & UART_MSR_DCTS)
+               uart_handle_cts_change(port, msr & UART_MSR_CTS);
+
+       wake_up_interruptible(&port->info->delta_msr_wait);
+}
+
+static inline void transmit_chars(struct uart_port *port)
+{
+       struct circ_buf *xmit;
+       int max_count = TX_MAX_COUNT;
+
+       xmit = &port->info->xmit;
+
+       if (port->x_char) {
+               siu_write(port, UART_TX, port->x_char);
+               port->icount.tx++;
+               port->x_char = 0;
+               return;
+       }
+
+       if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+               siu_stop_tx(port, 0);
+               return;
+       }
+
+       do {
+               siu_write(port, UART_TX, xmit->buf[xmit->tail]);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               port->icount.tx++;
+               if (uart_circ_empty(xmit))
+                       break;
+       } while (max_count-- > 0);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(port);
+
+       if (uart_circ_empty(xmit))
+               siu_stop_tx(port, 0);
+}
+
+static irqreturn_t siu_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct uart_port *port;
+       uint8_t iir, lsr;
+
+       port = (struct uart_port *)dev_id;
+
+       iir = siu_read(port, UART_IIR);
+       if (iir & UART_IIR_NO_INT)
+               return IRQ_NONE;
+
+       lsr = siu_read(port, UART_LSR);
+       if (lsr & UART_LSR_DR)
+               receive_chars(port, &lsr, regs);
+
+       check_modem_status(port);
+
+       if (lsr & UART_LSR_THRE)
+               transmit_chars(port);
+
+       return IRQ_HANDLED;
+}
+
+static int siu_startup(struct uart_port *port)
+{
+       int retval;
+
+       if (port->membase == NULL)
+               return -ENODEV;
+
+       siu_clear_fifo(port);
+
+       (void)siu_read(port, UART_LSR);
+       (void)siu_read(port, UART_RX);
+       (void)siu_read(port, UART_IIR);
+       (void)siu_read(port, UART_MSR);
+
+       if (siu_read(port, UART_LSR) == 0xff)
+               return -ENODEV;
+
+       retval = request_irq(port->irq, siu_interrupt, 0, siu_type_name(port), port);
+       if (retval)
+               return retval;
+
+       if (port->type == PORT_VR41XX_DSIU)
+               vr41xx_enable_dsiuint(DSIUINT_ALL);
+
+       siu_write(port, UART_LCR, UART_LCR_WLEN8);
+
+       spin_lock_irq(&port->lock);
+       siu_set_mctrl(port, port->mctrl);
+       spin_unlock_irq(&port->lock);
+
+       siu_write(port, UART_IER, UART_IER_RLSI | UART_IER_RDI);
+
+       (void)siu_read(port, UART_LSR);
+       (void)siu_read(port, UART_RX);
+       (void)siu_read(port, UART_IIR);
+       (void)siu_read(port, UART_MSR);
+
+       return 0;
+}
+
+static void siu_shutdown(struct uart_port *port)
+{
+       unsigned long flags;
+       uint8_t lcr;
+
+       siu_write(port, UART_IER, 0);
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       port->mctrl &= ~TIOCM_OUT2;
+       siu_set_mctrl(port, port->mctrl);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       lcr = siu_read(port, UART_LCR);
+       lcr &= ~UART_LCR_SBC;
+       siu_write(port, UART_LCR, lcr);
+
+       siu_clear_fifo(port);
+
+       (void)siu_read(port, UART_RX);
+
+       if (port->type == PORT_VR41XX_DSIU)
+               vr41xx_disable_dsiuint(DSIUINT_ALL);
+
+       free_irq(port->irq, port);
+}
+
+static void siu_set_termios(struct uart_port *port, struct termios *new,
+                            struct termios *old)
+{
+       tcflag_t c_cflag, c_iflag;
+       uint8_t lcr, fcr, ier;
+       unsigned int baud, quot;
+       unsigned long flags;
+
+       c_cflag = new->c_cflag;
+       switch (c_cflag & CSIZE) {
+       case CS5:
+               lcr = UART_LCR_WLEN5;
+               break;
+       case CS6:
+               lcr = UART_LCR_WLEN6;
+               break;
+       case CS7:
+               lcr = UART_LCR_WLEN7;
+               break;
+       default:
+               lcr = UART_LCR_WLEN8;
+               break;
+       }
+
+       if (c_cflag & CSTOPB)
+               lcr |= UART_LCR_STOP;
+       if (c_cflag & PARENB)
+               lcr |= UART_LCR_PARITY;
+       if ((c_cflag & PARODD) != PARODD)
+               lcr |= UART_LCR_EPAR;
+       if (c_cflag & CMSPAR)
+               lcr |= UART_LCR_SPAR;
+
+       baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16);
+       quot = uart_get_divisor(port, baud);
+
+       fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       uart_update_timeout(port, c_cflag, baud);
+
+       c_iflag = new->c_iflag;
+
+       port->read_status_mask = UART_LSR_THRE | UART_LSR_OE | UART_LSR_DR;
+       if (c_iflag & INPCK)
+               port->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+       if (c_iflag & (BRKINT | PARMRK))
+               port->read_status_mask |= UART_LSR_BI;
+
+       port->ignore_status_mask = 0;
+       if (c_iflag & IGNPAR)
+               port->ignore_status_mask |= UART_LSR_FE | UART_LSR_PE;
+       if (c_iflag & IGNBRK) {
+               port->ignore_status_mask |= UART_LSR_BI;
+               if (c_iflag & IGNPAR)
+                       port->ignore_status_mask |= UART_LSR_OE;
+       }
+
+       if ((c_cflag & CREAD) == 0)
+               port->ignore_status_mask |= UART_LSR_DR;
+
+       ier = siu_read(port, UART_IER);
+       ier &= ~UART_IER_MSI;
+       if (UART_ENABLE_MS(port, c_cflag))
+               ier |= UART_IER_MSI;
+       siu_write(port, UART_IER, ier);
+
+       siu_write(port, UART_LCR, lcr | UART_LCR_DLAB);
+
+       siu_write(port, UART_DLL, (uint8_t)quot);
+       siu_write(port, UART_DLM, (uint8_t)(quot >> 8));
+
+       siu_write(port, UART_LCR, lcr);
+
+       siu_write(port, UART_FCR, fcr);
+
+       siu_set_mctrl(port, port->mctrl);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void siu_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
+{
+       switch (state) {
+       case 0:
+               switch (port->type) {
+               case PORT_VR41XX_SIU:
+                       vr41xx_supply_clock(SIU_CLOCK);
+                       break;
+               case PORT_VR41XX_DSIU:
+                       vr41xx_supply_clock(DSIU_CLOCK);
+                       break;
+               }
+               break;
+       case 3:
+               switch (port->type) {
+               case PORT_VR41XX_SIU:
+                       vr41xx_mask_clock(SIU_CLOCK);
+                       break;
+               case PORT_VR41XX_DSIU:
+                       vr41xx_mask_clock(DSIU_CLOCK);
+                       break;
+               }
+               break;
+       }
+}
+
+static const char *siu_type(struct uart_port *port)
+{
+       return siu_type_name(port);
+}
+
+static void siu_release_port(struct uart_port *port)
+{
+       unsigned long size;
+
+       if (port->flags & UPF_IOREMAP) {
+               iounmap(port->membase);
+               port->membase = NULL;
+       }
+
+       size = siu_port_size(port);
+       release_mem_region(port->mapbase, size);
+}
+
+static int siu_request_port(struct uart_port *port)
+{
+       unsigned long size;
+       struct resource *res;
+
+       size = siu_port_size(port);
+       res = request_mem_region(port->mapbase, size, siu_type_name(port));
+       if (res == NULL)
+               return -EBUSY;
+
+       if (port->flags & UPF_IOREMAP) {
+               port->membase = ioremap(port->mapbase, size);
+               if (port->membase == NULL) {
+                       release_resource(res);
+                       return -ENOMEM;
+               }
+       }
+
+       return 0;
+}
+
+static void siu_config_port(struct uart_port *port, int flags)
+{
+       if (flags & UART_CONFIG_TYPE) {
+               port->type = siu_check_type(port);
+               (void)siu_request_port(port);
+       }
+}
+
+static int siu_verify_port(struct uart_port *port, struct serial_struct *serial)
+{
+       if (port->type != PORT_VR41XX_SIU && port->type != PORT_VR41XX_DSIU)
+               return -EINVAL;
+       if (port->irq != serial->irq)
+               return -EINVAL;
+       if (port->iotype != serial->io_type)
+               return -EINVAL;
+       if (port->mapbase != (unsigned long)serial->iomem_base)
+               return -EINVAL;
+
+       return 0;
+}
+
+static struct uart_ops siu_uart_ops = {
+       .tx_empty       = siu_tx_empty,
+       .set_mctrl      = siu_set_mctrl,
+       .get_mctrl      = siu_get_mctrl,
+       .stop_tx        = siu_stop_tx,
+       .start_tx       = siu_start_tx,
+       .stop_rx        = siu_stop_rx,
+       .enable_ms      = siu_enable_ms,
+       .break_ctl      = siu_break_ctl,
+       .startup        = siu_startup,
+       .shutdown       = siu_shutdown,
+       .set_termios    = siu_set_termios,
+       .pm             = siu_pm,
+       .type           = siu_type,
+       .release_port   = siu_release_port,
+       .request_port   = siu_request_port,
+       .config_port    = siu_config_port,
+       .verify_port    = siu_verify_port,
+};
+
+static int siu_init_ports(void)
+{
+       const struct siu_port *siu;
+       struct uart_port *port;
+       int i, num;
+
+       switch (current_cpu_data.cputype) {
+       case CPU_VR4111:
+       case CPU_VR4121:
+               siu = siu_type1_ports;
+               break;
+       case CPU_VR4122:
+       case CPU_VR4131:
+       case CPU_VR4133:
+               siu = siu_type2_ports;
+               break;
+       default:
+               return 0;
+       }
+
+       port = siu_uart_ports;
+       num = siu_probe_ports();
+       for (i = 0; i < num; i++) {
+               spin_lock_init(&port->lock);
+               port->irq = siu->irq;
+               port->uartclk = SIU_BAUD_BASE * 16;
+               port->fifosize = 16;
+               port->regshift = 0;
+               port->iotype = UPIO_MEM;
+               port->flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
+               port->type = siu->type;
+               port->line = i;
+               port->mapbase = siu->start;
+               siu++;
+               port++;
+       }
+
+       return num;
+}
+
+#ifdef CONFIG_SERIAL_VR41XX_CONSOLE
+
+#define BOTH_EMPTY     (UART_LSR_TEMT | UART_LSR_THRE)
+
+static void wait_for_xmitr(struct uart_port *port)
+{
+       int timeout = 10000;
+       uint8_t lsr, msr;
+
+       do {
+               lsr = siu_read(port, UART_LSR);
+               if (lsr & UART_LSR_BI)
+                       lsr_break_flag[port->line] = UART_LSR_BI;
+
+               if ((lsr & BOTH_EMPTY) == BOTH_EMPTY)
+                       break;
+       } while (timeout-- > 0);
+
+       if (port->flags & UPF_CONS_FLOW) {
+               timeout = 1000000;
+
+               do {
+                       msr = siu_read(port, UART_MSR);
+                       if ((msr & UART_MSR_CTS) != 0)
+                               break;
+               } while (timeout-- > 0);
+       }
+}
+
+static void siu_console_write(struct console *con, const char *s, unsigned count)
+{
+       struct uart_port *port;
+       uint8_t ier;
+       unsigned i;
+
+       port = &siu_uart_ports[con->index];
+
+       ier = siu_read(port, UART_IER);
+       siu_write(port, UART_IER, 0);
+
+       for (i = 0; i < count && *s != '\0'; i++, s++) {
+               wait_for_xmitr(port);
+               siu_write(port, UART_TX, *s);
+               if (*s == '\n') {
+                       wait_for_xmitr(port);
+                       siu_write(port, UART_TX, '\r');
+               }
+       }
+
+       wait_for_xmitr(port);
+       siu_write(port, UART_IER, ier);
+}
+
+static int siu_console_setup(struct console *con, char *options)
+{
+       struct uart_port *port;
+       int baud = 9600;
+       int parity = 'n';
+       int bits = 8;
+       int flow = 'n';
+
+       if (con->index >= SIU_PORTS_MAX)
+               con->index = 0;
+
+       port = &siu_uart_ports[con->index];
+       if (port->membase == NULL) {
+               if (port->mapbase == 0)
+                       return -ENODEV;
+               port->membase = ioremap(port->mapbase, siu_port_size(port));
+       }
+
+       vr41xx_select_siu_interface(SIU_INTERFACE_RS232C);
+
+       if (options != NULL)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+       return uart_set_options(port, con, baud, parity, bits, flow);
+}
+
+static struct uart_driver siu_uart_driver;
+
+static struct console siu_console = {
+       .name   = "ttyVR",
+       .write  = siu_console_write,
+       .device = uart_console_device,
+       .setup  = siu_console_setup,
+       .flags  = CON_PRINTBUFFER,
+       .index  = -1,
+       .data   = &siu_uart_driver,
+};
+
+static int __devinit siu_console_init(void)
+{
+       struct uart_port *port;
+       int num, i;
+
+       num = siu_init_ports();
+       if (num <= 0)
+               return -ENODEV;
+
+       for (i = 0; i < num; i++) {
+               port = &siu_uart_ports[i];
+               port->ops = &siu_uart_ops;
+       }
+
+       register_console(&siu_console);
+
+       return 0;
+}
+
+console_initcall(siu_console_init);
+
+#define SERIAL_VR41XX_CONSOLE  &siu_console
+#else
+#define SERIAL_VR41XX_CONSOLE  NULL
+#endif
+
+static struct uart_driver siu_uart_driver = {
+       .owner          = THIS_MODULE,
+       .driver_name    = "SIU",
+       .dev_name       = "ttyVR",
+       .devfs_name     = "ttvr/",
+       .major          = SIU_MAJOR,
+       .minor          = SIU_MINOR_BASE,
+       .cons           = SERIAL_VR41XX_CONSOLE,
+};
+
+static int siu_probe(struct device *dev)
+{
+       struct uart_port *port;
+       int num, i, retval;
+
+       num = siu_init_ports();
+       if (num <= 0)
+               return -ENODEV;
+
+       siu_uart_driver.nr = num;
+       retval = uart_register_driver(&siu_uart_driver);
+       if (retval)
+               return retval;
+
+       for (i = 0; i < num; i++) {
+               port = &siu_uart_ports[i];
+               port->ops = &siu_uart_ops;
+               port->dev = dev;
+
+               retval = uart_add_one_port(&siu_uart_driver, port);
+               if (retval < 0) {
+                       port->dev = NULL;
+                       break;
+               }
+       }
+
+       if (i == 0 && retval < 0) {
+               uart_unregister_driver(&siu_uart_driver);
+               return retval;
+       }
+
+       return 0;
+}
+
+static int siu_remove(struct device *dev)
+{
+       struct uart_port *port;
+       int i;
+
+       for (i = 0; i < siu_uart_driver.nr; i++) {
+               port = &siu_uart_ports[i];
+               if (port->dev == dev) {
+                       uart_remove_one_port(&siu_uart_driver, port);
+                       port->dev = NULL;
+               }
+       }
+
+       uart_unregister_driver(&siu_uart_driver);
+
+       return 0;
+}
+
+static int siu_suspend(struct device *dev, pm_message_t state, u32 level)
+{
+       struct uart_port *port;
+       int i;
+
+       if (level != SUSPEND_DISABLE)
+               return 0;
+
+       for (i = 0; i < siu_uart_driver.nr; i++) {
+               port = &siu_uart_ports[i];
+               if ((port->type == PORT_VR41XX_SIU ||
+                    port->type == PORT_VR41XX_DSIU) && port->dev == dev)
+                       uart_suspend_port(&siu_uart_driver, port);
+
+       }
+
+       return 0;
+}
+
+static int siu_resume(struct device *dev, u32 level)
+{
+       struct uart_port *port;
+       int i;
+
+       if (level != RESUME_ENABLE)
+               return 0;
+
+       for (i = 0; i < siu_uart_driver.nr; i++) {
+               port = &siu_uart_ports[i];
+               if ((port->type == PORT_VR41XX_SIU ||
+                    port->type == PORT_VR41XX_DSIU) && port->dev == dev)
+                       uart_resume_port(&siu_uart_driver, port);
+       }
+
+       return 0;
+}
+
+static struct platform_device *siu_platform_device;
+
+static struct device_driver siu_device_driver = {
+       .name           = "SIU",
+       .bus            = &platform_bus_type,
+       .probe          = siu_probe,
+       .remove         = siu_remove,
+       .suspend        = siu_suspend,
+       .resume         = siu_resume,
+};
+
+static int __devinit vr41xx_siu_init(void)
+{
+       int retval;
+
+       siu_platform_device = platform_device_register_simple("SIU", -1, NULL, 0);
+       if (IS_ERR(siu_platform_device))
+               return PTR_ERR(siu_platform_device);
+
+       retval = driver_register(&siu_device_driver);
+       if (retval < 0)
+               platform_device_unregister(siu_platform_device);
+
+       return retval;
+}
+
+static void __devexit vr41xx_siu_exit(void)
+{
+       driver_unregister(&siu_device_driver);
+
+       platform_device_unregister(siu_platform_device);
+}
+
+module_init(vr41xx_siu_init);
+module_exit(vr41xx_siu_exit);
diff --git a/drivers/sn/Makefile b/drivers/sn/Makefile
new file mode 100644 (file)
index 0000000..631e549
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile for the Altix device drivers.
+#
+#
+
+obj-$(CONFIG_BLK_DEV_SGIIOC4) += ioc4.o
diff --git a/drivers/sn/ioc4.c b/drivers/sn/ioc4.c
new file mode 100644 (file)
index 0000000..d9e4ee2
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+/*
+ * This file contains a shim driver for the IOC4 IDE and serial drivers.
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/ioc4_common.h>
+#include <linux/ide.h>
+
+
+static int __devinit
+ioc4_probe_one(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+{
+       int ret;
+
+       if ((ret = pci_enable_device(pdev))) {
+               printk(KERN_WARNING
+                        "%s: Failed to enable device with "
+                               "pci_dev 0x%p... returning\n",
+                               __FUNCTION__, (void *)pdev);
+               return ret;
+       }
+       pci_set_master(pdev);
+
+       /* attach each sub-device */
+       ret = ioc4_ide_attach_one(pdev, pci_id);
+       if (ret)
+               return ret;
+       return ioc4_serial_attach_one(pdev, pci_id);
+}
+
+/* pci device struct */
+static struct pci_device_id ioc4_s_id_table[] = {
+       {PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4, PCI_ANY_ID,
+        PCI_ANY_ID, 0x0b4000, 0xFFFFFF},
+       {0}
+};
+MODULE_DEVICE_TABLE(pci, ioc4_s_id_table);
+
+static struct pci_driver __devinitdata ioc4_s_driver = {
+       .name   = "IOC4",
+       .id_table = ioc4_s_id_table,
+       .probe  = ioc4_probe_one,
+};
+
+static int __devinit ioc4_detect(void)
+{
+       ioc4_serial_init();
+
+       return pci_register_driver(&ioc4_s_driver);
+}
+module_init(ioc4_detect);
+
+MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) <pfg@sgi.com>");
+MODULE_DESCRIPTION("PCI driver module for SGI IOC4 Base-IO Card");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c
new file mode 100644 (file)
index 0000000..17964c3
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * 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
+ * (C) Copyright 2003-2005 MontaVista Software Inc.
+ * 
+ * Bus Glue for PPC On-Chip OHCI driver
+ * Tested on Freescale MPC5200 and IBM STB04xxx
+ *
+ * Modified by Dale Farnsworth <dale@farnsworth.org> from ohci-sa1111.c
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <asm/usb.h>
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_hcd_ppc_soc_probe - initialize On-Chip 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.
+ *
+ * Store this function in the HCD's struct pci_driver as probe().
+ */
+static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver,
+                         struct platform_device *pdev)
+{
+       int retval;
+       struct usb_hcd *hcd;
+       struct ohci_hcd *ohci;
+       struct resource *res;
+       int irq;
+       struct usb_hcd_platform_data *pd = pdev->dev.platform_data;
+
+       pr_debug("initializing PPC-SOC USB Controller\n");
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res) {
+               pr_debug(__FILE__ ": no irq\n");
+               return -ENODEV;
+       }
+       irq = res->start;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               pr_debug(__FILE__ ": no reg addr\n");
+               return -ENODEV;
+       }
+
+       hcd = usb_create_hcd(driver, &pdev->dev, "PPC-SOC USB");
+       if (!hcd)
+               return -ENOMEM;
+       hcd->rsrc_start = res->start;
+       hcd->rsrc_len = res->end - res->start + 1;
+
+       if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+               pr_debug(__FILE__ ": request_mem_region failed\n");
+               retval = -EBUSY;
+               goto err1;
+       }
+
+       hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+       if (!hcd->regs) {
+               pr_debug(__FILE__ ": ioremap failed\n");
+               retval = -ENOMEM;
+               goto err2;
+       }
+
+       if (pd->start && (retval = pd->start(pdev)))
+               goto err3;
+
+       ohci = hcd_to_ohci(hcd);
+       ohci->flags |= OHCI_BIG_ENDIAN;
+       ohci_hcd_init(ohci);
+
+       retval = usb_add_hcd(hcd, irq, SA_INTERRUPT);
+       if (retval == 0)
+               return retval;
+
+       pr_debug("Removing PPC-SOC USB Controller\n");
+       if (pd && pd->stop)
+               pd->stop(pdev);
+ err3:
+       iounmap(hcd->regs);
+ err2:
+       release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ err1:
+       usb_put_hcd(hcd);
+       return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_ppc_soc_remove - shutdown processing for On-Chip HCDs
+ * @pdev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_ppc_soc_probe(), first invoking
+ * the HCD's stop() method.  It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+static void usb_hcd_ppc_soc_remove(struct usb_hcd *hcd,
+               struct platform_device *pdev)
+{
+       struct usb_hcd_platform_data *pd = pdev->dev.platform_data;
+
+       usb_remove_hcd(hcd);
+
+       pr_debug("stopping PPC-SOC USB Controller\n");
+       if (pd && pd->stop)
+               pd->stop(pdev);
+
+       iounmap(hcd->regs);
+       release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+       usb_hcd_put(hcd);
+}
+
+static int __devinit
+ohci_ppc_soc_start(struct usb_hcd *hcd)
+{
+       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+       int             ret;
+
+       if ((ret = ohci_init(ohci)) < 0)
+               return ret;
+
+       if ((ret = ohci_run(ohci)) < 0) {
+               err("can't start %s", ohci_to_hcd(ohci)->self.bus_name);
+               ohci_stop(hcd);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct hc_driver ohci_ppc_soc_hc_driver = {
+       .description =          hcd_name,
+       .hcd_priv_size =        sizeof(struct ohci_hcd),
+
+       /*
+        * generic hardware linkage
+        */
+       .irq =                  ohci_irq,
+       .flags =                HCD_USB11 | HCD_MEMORY,
+
+       /*
+        * basic lifecycle operations
+        */
+       .start =                ohci_ppc_soc_start,
+       .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,
+#ifdef CONFIG_USB_SUSPEND
+       .hub_suspend =          ohci_hub_suspend,
+       .hub_resume =           ohci_hub_resume,
+#endif
+       .start_port_reset =     ohci_start_port_reset,
+};
+
+static int ohci_hcd_ppc_soc_drv_probe(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       int ret;
+
+       if (usb_disabled())
+               return -ENODEV;
+
+       ret = usb_hcd_ppc_soc_probe(&ohci_ppc_soc_hc_driver, pdev);
+       return ret;
+}
+
+static int ohci_hcd_ppc_soc_drv_remove(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+       usb_hcd_ppc_soc_remove(hcd, pdev);
+       return 0;
+}
+
+static struct device_driver ohci_hcd_ppc_soc_driver = {
+       .name           = "ppc-soc-ohci",
+       .bus            = &platform_bus_type,
+       .probe          = ohci_hcd_ppc_soc_drv_probe,
+       .remove         = ohci_hcd_ppc_soc_drv_remove,
+#if    defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM)
+       /*.suspend      = ohci_hcd_ppc_soc_drv_suspend,*/
+       /*.resume       = ohci_hcd_ppc_soc_drv_resume,*/
+#endif
+};
+
+static int __init ohci_hcd_ppc_soc_init(void)
+{
+       pr_debug(DRIVER_INFO " (PPC SOC)\n");
+       pr_debug("block sizes: ed %d td %d\n", sizeof(struct ed),
+                                                       sizeof(struct td));
+
+       return driver_register(&ohci_hcd_ppc_soc_driver);
+}
+
+static void __exit ohci_hcd_ppc_soc_cleanup(void)
+{
+       driver_unregister(&ohci_hcd_ppc_soc_driver);
+}
+
+module_init(ohci_hcd_ppc_soc_init);
+module_exit(ohci_hcd_ppc_soc_cleanup);
diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c
new file mode 100644 (file)
index 0000000..6e17326
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * PCMCIA driver for SL811HS (as found in REX-CFU1U)
+ * Filename: sl811_cs.c
+ * Author:   Yukio Yamamoto
+ *
+ *  Port to sl811-hcd and 2.6.x by
+ *    Botond Botyanszki <boti@rocketmail.com>
+ *    Simon Pickering
+ *
+ *  Last update: 2005-05-12
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+
+#include <linux/usb_sl811.h>
+
+MODULE_AUTHOR("Botond Botyanszki");
+MODULE_DESCRIPTION("REX-CFU1U PCMCIA driver for 2.6");
+MODULE_LICENSE("GPL");
+
+
+/*====================================================================*/
+/* MACROS                                                             */
+/*====================================================================*/
+
+#if defined(DEBUG) || defined(CONFIG_USB_DEBUG) || defined(PCMCIA_DEBUG)
+
+static int pc_debug = 0;
+module_param(pc_debug, int, 0644);
+
+#define DBG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG "sl811_cs: " args)
+
+#else
+#define DBG(n, args...) do{}while(0)
+#endif /* no debugging */
+
+#define INFO(args...) printk(KERN_INFO "sl811_cs: " args)
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444)
+
+#define CS_CHECK(fn, ret) \
+       do { \
+               last_fn = (fn); \
+               if ((last_ret = (ret)) != 0) \
+                       goto cs_failed; \
+       } while (0)
+
+/*====================================================================*/
+/* VARIABLES                                                          */
+/*====================================================================*/
+
+static const char driver_name[DEV_NAME_LEN]  = "sl811_cs";
+
+static dev_link_t *dev_list = NULL;
+
+static int irq_list[4] = { -1 };
+static int irq_list_count;
+
+module_param_array(irq_list, int, &irq_list_count, 0444);
+
+INT_MODULE_PARM(irq_mask, 0xdeb8);
+
+typedef struct local_info_t {
+       dev_link_t              link;
+       dev_node_t              node;
+} local_info_t;
+
+/*====================================================================*/
+
+static void release_platform_dev(struct device * dev)
+{
+       DBG(0, "sl811_cs platform_dev release\n");
+       dev->parent = NULL;
+}
+
+static struct sl811_platform_data platform_data = {
+       .potpg          = 100,
+       .power          = 50,           /* == 100mA */
+       // .reset       = ... FIXME:  invoke CF reset on the card
+};
+
+static struct resource resources[] = {
+       [0] = {
+               .flags  = IORESOURCE_IRQ,
+       },
+       [1] = {
+               // .name   = "address",
+               .flags  = IORESOURCE_IO,
+       },
+       [2] = {
+               // .name   = "data",
+               .flags  = IORESOURCE_IO,
+       },
+};
+
+extern struct device_driver sl811h_driver;
+
+static struct platform_device platform_dev = {
+       .id                     = -1,
+       .dev = {
+               .platform_data = &platform_data,
+               .release       = release_platform_dev,
+       },
+       .resource               = resources,
+       .num_resources          = ARRAY_SIZE(resources),
+};
+
+static int sl811_hc_init(struct device *parent, ioaddr_t base_addr, int irq)
+{
+       if (platform_dev.dev.parent)
+               return -EBUSY;
+       platform_dev.dev.parent = parent;
+
+       /* finish seting up the platform device */
+       resources[0].start = irq;
+
+       resources[1].start = base_addr;
+       resources[1].end = base_addr;
+
+       resources[2].start = base_addr + 1;
+       resources[2].end   = base_addr + 1;
+
+       /* The driver core will probe for us.  We know sl811-hcd has been
+        * initialized already because of the link order dependency.
+        */
+       platform_dev.name = sl811h_driver.name;
+       return platform_device_register(&platform_dev);
+}
+
+/*====================================================================*/
+
+static void sl811_cs_detach(dev_link_t *link)
+{
+       dev_link_t **linkp;
+
+       DBG(0, "sl811_cs_detach(0x%p)\n", link);
+
+       /* Locate device structure */
+       for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) {
+               if (*linkp == link)
+                       break;
+       }
+       if (*linkp == NULL)
+               return;
+
+       /* Break the link with Card Services */
+       if (link->handle)
+               pcmcia_deregister_client(link->handle);
+
+       /* Unlink device structure, and free it */
+       *linkp = link->next;
+       /* This points to the parent local_info_t struct */
+       kfree(link->priv);
+}
+
+static void sl811_cs_release(dev_link_t * link)
+{
+
+       DBG(0, "sl811_cs_release(0x%p)\n", link);
+
+       if (link->open) {
+               DBG(1, "sl811_cs: release postponed, '%s' still open\n",
+                   link->dev->dev_name);
+               link->state |= DEV_STALE_CONFIG;
+               return;
+       }
+
+       /* Unlink the device chain */
+       link->dev = NULL;
+
+       platform_device_unregister(&platform_dev);
+       pcmcia_release_configuration(link->handle);
+       if (link->io.NumPorts1)
+               pcmcia_release_io(link->handle, &link->io);
+       if (link->irq.AssignedIRQ)
+               pcmcia_release_irq(link->handle, &link->irq);
+       link->state &= ~DEV_CONFIG;
+
+       if (link->state & DEV_STALE_LINK)
+               sl811_cs_detach(link);
+}
+
+static void sl811_cs_config(dev_link_t *link)
+{
+       client_handle_t         handle = link->handle;
+       struct device           *parent = &handle_to_dev(handle);
+       local_info_t            *dev = link->priv;
+       tuple_t                 tuple;
+       cisparse_t              parse;
+       int                     last_fn, last_ret;
+       u_char                  buf[64];
+       config_info_t           conf;
+       cistpl_cftable_entry_t  dflt = { 0 };
+
+       DBG(0, "sl811_cs_config(0x%p)\n", link);
+
+       tuple.DesiredTuple = CISTPL_CONFIG;
+       tuple.Attributes = 0;
+       tuple.TupleData = buf;
+       tuple.TupleDataMax = sizeof(buf);
+       tuple.TupleOffset = 0;
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+       CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+       link->conf.ConfigBase = parse.config.base;
+       link->conf.Present = parse.config.rmask[0];
+
+       /* Configure card */
+       link->state |= DEV_CONFIG;
+
+       /* Look up the current Vcc */
+       CS_CHECK(GetConfigurationInfo,
+                       pcmcia_get_configuration_info(handle, &conf));
+       link->conf.Vcc = conf.Vcc;
+
+       tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+       CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+       while (1) {
+               cistpl_cftable_entry_t  *cfg = &(parse.cftable_entry);
+
+               if (pcmcia_get_tuple_data(handle, &tuple) != 0
+                               || pcmcia_parse_tuple(handle, &tuple, &parse)
+                                               != 0)
+                       goto next_entry;
+
+               if (cfg->flags & CISTPL_CFTABLE_DEFAULT) {
+                       dflt = *cfg;
+               }
+
+               if (cfg->index == 0)
+                       goto next_entry;
+
+               link->conf.ConfigIndex = cfg->index;
+
+               /* Use power settings for Vcc and Vpp if present */
+               /*  Note that the CIS values need to be rescaled */
+               if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+                       if (cfg->vcc.param[CISTPL_POWER_VNOM]/10000
+                                       != conf.Vcc)
+                               goto next_entry;
+               } else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) {
+                       if (dflt.vcc.param[CISTPL_POWER_VNOM]/10000
+                                       != conf.Vcc)
+                               goto next_entry;
+               }
+
+               if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
+                       link->conf.Vpp1 = link->conf.Vpp2 =
+                               cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
+               else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
+                       link->conf.Vpp1 = link->conf.Vpp2 =
+                               dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
+
+               /* we need an interrupt */
+               if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
+                       link->conf.Attributes |= CONF_ENABLE_IRQ;
+
+               /* IO window settings */
+               link->io.NumPorts1 = link->io.NumPorts2 = 0;
+               if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
+                       cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
+
+                       link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+                       link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+                       link->io.BasePort1 = io->win[0].base;
+                       link->io.NumPorts1 = io->win[0].len;
+
+                       if (pcmcia_request_io(link->handle, &link->io) != 0)
+                               goto next_entry;
+               }
+               break;
+
+next_entry:
+               if (link->io.NumPorts1)
+                       pcmcia_release_io(link->handle, &link->io);
+               last_ret = pcmcia_get_next_tuple(handle, &tuple);
+       }
+
+       /* require an IRQ and two registers */
+       if (!link->io.NumPorts1 || link->io.NumPorts1 < 2)
+               goto cs_failed;
+       if (link->conf.Attributes & CONF_ENABLE_IRQ)
+               CS_CHECK(RequestIRQ,
+                       pcmcia_request_irq(link->handle, &link->irq));
+       else
+               goto cs_failed;
+
+       CS_CHECK(RequestConfiguration,
+               pcmcia_request_configuration(link->handle, &link->conf));
+
+       sprintf(dev->node.dev_name, driver_name);
+       dev->node.major = dev->node.minor = 0;
+       link->dev = &dev->node;
+
+       printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d",
+              dev->node.dev_name, link->conf.ConfigIndex,
+              link->conf.Vcc/10, link->conf.Vcc%10);
+       if (link->conf.Vpp1)
+               printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10);
+       printk(", irq %d", link->irq.AssignedIRQ);
+       printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+              link->io.BasePort1+link->io.NumPorts1-1);
+       printk("\n");
+
+       link->state &= ~DEV_CONFIG_PENDING;
+
+       if (sl811_hc_init(parent, link->io.BasePort1, link->irq.AssignedIRQ)
+                       < 0) {
+cs_failed:
+               printk("sl811_cs_config failed\n");
+               cs_error(link->handle, last_fn, last_ret);
+               sl811_cs_release(link);
+               link->state &= ~DEV_CONFIG_PENDING;
+       }
+}
+
+static int
+sl811_cs_event(event_t event, int priority, event_callback_args_t *args)
+{
+       dev_link_t *link = args->client_data;
+
+       DBG(1, "sl811_cs_event(0x%06x)\n", event);
+
+       switch (event) {
+       case CS_EVENT_CARD_REMOVAL:
+               link->state &= ~DEV_PRESENT;
+               if (link->state & DEV_CONFIG)
+                       sl811_cs_release(link);
+               break;
+
+       case CS_EVENT_CARD_INSERTION:
+               link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+               sl811_cs_config(link);
+               break;
+
+       case CS_EVENT_PM_SUSPEND:
+               link->state |= DEV_SUSPEND;
+               /* Fall through... */
+       case CS_EVENT_RESET_PHYSICAL:
+               if (link->state & DEV_CONFIG)
+                       pcmcia_release_configuration(link->handle);
+               break;
+
+       case CS_EVENT_PM_RESUME:
+               link->state &= ~DEV_SUSPEND;
+               /* Fall through... */
+       case CS_EVENT_CARD_RESET:
+               if (link->state & DEV_CONFIG)
+                       pcmcia_request_configuration(link->handle, &link->conf);
+               DBG(0, "reset sl811-hcd here?\n");
+               break;
+       }
+       return 0;
+}
+
+static dev_link_t *sl811_cs_attach(void)
+{
+       local_info_t *local;
+       dev_link_t *link;
+       client_reg_t client_reg;
+       int ret, i;
+
+       local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+       if (!local)
+               return NULL;
+       memset(local, 0, sizeof(local_info_t));
+       link = &local->link;
+       link->priv = local;
+
+       /* Initialize */
+       link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+       link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
+       if (irq_list[0] == -1)
+               link->irq.IRQInfo2 = irq_mask;
+       else
+               for (i = 0; i < irq_list_count; i++)
+                       link->irq.IRQInfo2 |= 1 << irq_list[i];
+       link->irq.Handler = NULL;
+
+       link->conf.Attributes = 0;
+       link->conf.Vcc = 33;
+       link->conf.IntType = INT_MEMORY_AND_IO;
+
+       /* Register with Card Services */
+       link->next = dev_list;
+       dev_list = link;
+       client_reg.dev_info = (dev_info_t *) &driver_name;
+       client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+       client_reg.EventMask =
+               CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+               CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+               CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+       client_reg.event_handler = &sl811_cs_event;
+       client_reg.Version = 0x0210;
+       client_reg.event_callback_args.client_data = link;
+       ret = pcmcia_register_client(&link->handle, &client_reg);
+       if (ret != CS_SUCCESS) {
+               cs_error(link->handle, RegisterClient, ret);
+               sl811_cs_detach(link);
+               return NULL;
+       }
+
+       return link;
+}
+
+static struct pcmcia_driver sl811_cs_driver = {
+       .owner          = THIS_MODULE,
+       .drv            = {
+               .name   = (char *)driver_name,
+       },
+       .attach         = sl811_cs_attach,
+       .detach         = sl811_cs_detach,
+};
+
+/*====================================================================*/
+
+static int __init init_sl811_cs(void)
+{
+       return pcmcia_register_driver(&sl811_cs_driver);
+}
+module_init(init_sl811_cs);
+
+static void __exit exit_sl811_cs(void)
+{
+       pcmcia_unregister_driver(&sl811_cs_driver);
+}
+module_exit(exit_sl811_cs);
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
new file mode 100644 (file)
index 0000000..2a7c195
--- /dev/null
@@ -0,0 +1,1539 @@
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * Maintainer: Alan Stern <stern@rowland.harvard.edu>
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com
+ * (C) Copyright 1999 Randy Dunlap
+ * (C) Copyright 1999 Georg Acher, acher@in.tum.de
+ * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de
+ * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch
+ * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at
+ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
+ *               support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
+ * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
+ * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
+ */
+
+static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb);
+static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb);
+static void uhci_remove_pending_urbps(struct uhci_hcd *uhci);
+static void uhci_free_pending_qhs(struct uhci_hcd *uhci);
+static void uhci_free_pending_tds(struct uhci_hcd *uhci);
+
+/*
+ * Technically, updating td->status here is a race, but it's not really a
+ * problem. The worst that can happen is that we set the IOC bit again
+ * generating a spurious interrupt. We could fix this by creating another
+ * QH and leaving the IOC bit always set, but then we would have to play
+ * games with the FSBR code to make sure we get the correct order in all
+ * the cases. I don't think it's worth the effort
+ */
+static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci)
+{
+       uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); 
+}
+
+static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci)
+{
+       uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC);
+}
+
+static inline void uhci_moveto_complete(struct uhci_hcd *uhci, 
+                                       struct urb_priv *urbp)
+{
+       list_move_tail(&urbp->urb_list, &uhci->complete_list);
+}
+
+static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci, struct usb_device *dev)
+{
+       dma_addr_t dma_handle;
+       struct uhci_td *td;
+
+       td = dma_pool_alloc(uhci->td_pool, GFP_ATOMIC, &dma_handle);
+       if (!td)
+               return NULL;
+
+       td->dma_handle = dma_handle;
+
+       td->link = UHCI_PTR_TERM;
+       td->buffer = 0;
+
+       td->frame = -1;
+       td->dev = dev;
+
+       INIT_LIST_HEAD(&td->list);
+       INIT_LIST_HEAD(&td->remove_list);
+       INIT_LIST_HEAD(&td->fl_list);
+
+       usb_get_dev(dev);
+
+       return td;
+}
+
+static inline void uhci_fill_td(struct uhci_td *td, u32 status,
+               u32 token, u32 buffer)
+{
+       td->status = cpu_to_le32(status);
+       td->token = cpu_to_le32(token);
+       td->buffer = cpu_to_le32(buffer);
+}
+
+/*
+ * We insert Isochronous URB's directly into the frame list at the beginning
+ */
+static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum)
+{
+       framenum &= (UHCI_NUMFRAMES - 1);
+
+       td->frame = framenum;
+
+       /* Is there a TD already mapped there? */
+       if (uhci->fl->frame_cpu[framenum]) {
+               struct uhci_td *ftd, *ltd;
+
+               ftd = uhci->fl->frame_cpu[framenum];
+               ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
+
+               list_add_tail(&td->fl_list, &ftd->fl_list);
+
+               td->link = ltd->link;
+               wmb();
+               ltd->link = cpu_to_le32(td->dma_handle);
+       } else {
+               td->link = uhci->fl->frame[framenum];
+               wmb();
+               uhci->fl->frame[framenum] = cpu_to_le32(td->dma_handle);
+               uhci->fl->frame_cpu[framenum] = td;
+       }
+}
+
+static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td)
+{
+       /* If it's not inserted, don't remove it */
+       if (td->frame == -1 && list_empty(&td->fl_list))
+               return;
+
+       if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) {
+               if (list_empty(&td->fl_list)) {
+                       uhci->fl->frame[td->frame] = td->link;
+                       uhci->fl->frame_cpu[td->frame] = NULL;
+               } else {
+                       struct uhci_td *ntd;
+
+                       ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list);
+                       uhci->fl->frame[td->frame] = cpu_to_le32(ntd->dma_handle);
+                       uhci->fl->frame_cpu[td->frame] = ntd;
+               }
+       } else {
+               struct uhci_td *ptd;
+
+               ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list);
+               ptd->link = td->link;
+       }
+
+       wmb();
+       td->link = UHCI_PTR_TERM;
+
+       list_del_init(&td->fl_list);
+       td->frame = -1;
+}
+
+/*
+ * Inserts a td list into qh.
+ */
+static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, __le32 breadth)
+{
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       struct uhci_td *td;
+       __le32 *plink;
+
+       /* Ordering isn't important here yet since the QH hasn't been */
+       /* inserted into the schedule yet */
+       plink = &qh->element;
+       list_for_each_entry(td, &urbp->td_list, list) {
+               *plink = cpu_to_le32(td->dma_handle) | breadth;
+               plink = &td->link;
+       }
+       *plink = UHCI_PTR_TERM;
+}
+
+static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
+{
+       if (!list_empty(&td->list))
+               dev_warn(uhci_dev(uhci), "td %p still in list!\n", td);
+       if (!list_empty(&td->remove_list))
+               dev_warn(uhci_dev(uhci), "td %p still in remove_list!\n", td);
+       if (!list_empty(&td->fl_list))
+               dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td);
+
+       if (td->dev)
+               usb_put_dev(td->dev);
+
+       dma_pool_free(uhci->td_pool, td, td->dma_handle);
+}
+
+static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, struct usb_device *dev)
+{
+       dma_addr_t dma_handle;
+       struct uhci_qh *qh;
+
+       qh = dma_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle);
+       if (!qh)
+               return NULL;
+
+       qh->dma_handle = dma_handle;
+
+       qh->element = UHCI_PTR_TERM;
+       qh->link = UHCI_PTR_TERM;
+
+       qh->dev = dev;
+       qh->urbp = NULL;
+
+       INIT_LIST_HEAD(&qh->list);
+       INIT_LIST_HEAD(&qh->remove_list);
+
+       usb_get_dev(dev);
+
+       return qh;
+}
+
+static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+       if (!list_empty(&qh->list))
+               dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh);
+       if (!list_empty(&qh->remove_list))
+               dev_warn(uhci_dev(uhci), "qh %p still in remove_list!\n", qh);
+
+       if (qh->dev)
+               usb_put_dev(qh->dev);
+
+       dma_pool_free(uhci->qh_pool, qh, qh->dma_handle);
+}
+
+/*
+ * Append this urb's qh after the last qh in skelqh->list
+ *
+ * Note that urb_priv.queue_list doesn't have a separate queue head;
+ * it's a ring with every element "live".
+ */
+static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb)
+{
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       struct urb_priv *turbp;
+       struct uhci_qh *lqh;
+
+       /* Grab the last QH */
+       lqh = list_entry(skelqh->list.prev, struct uhci_qh, list);
+
+       /* Point to the next skelqh */
+       urbp->qh->link = lqh->link;
+       wmb();                          /* Ordering is important */
+
+       /*
+        * Patch QHs for previous endpoint's queued URBs?  HC goes
+        * here next, not to the next skelqh it now points to.
+        *
+        *    lqh --> td ... --> qh ... --> td --> qh ... --> td
+        *     |                 |                 |
+        *     v                 v                 v
+        *     +<----------------+-----------------+
+        *     v
+        *    newqh --> td ... --> td
+        *     |
+        *     v
+        *    ...
+        *
+        * The HC could see (and use!) any of these as we write them.
+        */
+       lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
+       if (lqh->urbp) {
+               list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list)
+                       turbp->qh->link = lqh->link;
+       }
+
+       list_add_tail(&urbp->qh->list, &skelqh->list);
+}
+
+/*
+ * Start removal of QH from schedule; it finishes next frame.
+ * TDs should be unlinked before this is called.
+ */
+static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+       struct uhci_qh *pqh;
+       __le32 newlink;
+
+       if (!qh)
+               return;
+
+       /*
+        * Only go through the hoops if it's actually linked in
+        */
+       if (!list_empty(&qh->list)) {
+
+               /* If our queue is nonempty, make the next URB the head */
+               if (!list_empty(&qh->urbp->queue_list)) {
+                       struct urb_priv *nurbp;
+
+                       nurbp = list_entry(qh->urbp->queue_list.next,
+                                       struct urb_priv, queue_list);
+                       nurbp->queued = 0;
+                       list_add(&nurbp->qh->list, &qh->list);
+                       newlink = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH;
+               } else
+                       newlink = qh->link;
+
+               /* Fix up the previous QH's queue to link to either
+                * the new head of this queue or the start of the
+                * next endpoint's queue. */
+               pqh = list_entry(qh->list.prev, struct uhci_qh, list);
+               pqh->link = newlink;
+               if (pqh->urbp) {
+                       struct urb_priv *turbp;
+
+                       list_for_each_entry(turbp, &pqh->urbp->queue_list,
+                                       queue_list)
+                               turbp->qh->link = newlink;
+               }
+               wmb();
+
+               /* Leave qh->link in case the HC is on the QH now, it will */
+               /* continue the rest of the schedule */
+               qh->element = UHCI_PTR_TERM;
+
+               list_del_init(&qh->list);
+       }
+
+       list_del_init(&qh->urbp->queue_list);
+       qh->urbp = NULL;
+
+       uhci_get_current_frame_number(uhci);
+       if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age) {
+               uhci_free_pending_qhs(uhci);
+               uhci->qh_remove_age = uhci->frame_number;
+       }
+
+       /* Check to see if the remove list is empty. Set the IOC bit */
+       /* to force an interrupt so we can remove the QH */
+       if (list_empty(&uhci->qh_remove_list))
+               uhci_set_next_interrupt(uhci);
+
+       list_add(&qh->remove_list, &uhci->qh_remove_list);
+}
+
+static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle)
+{
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       struct uhci_td *td;
+
+       list_for_each_entry(td, &urbp->td_list, list) {
+               if (toggle)
+                       td->token |= cpu_to_le32(TD_TOKEN_TOGGLE);
+               else
+                       td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE);
+
+               toggle ^= 1;
+       }
+
+       return toggle;
+}
+
+/* This function will append one URB's QH to another URB's QH. This is for */
+/* queuing interrupt, control or bulk transfers */
+static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb)
+{
+       struct urb_priv *eurbp, *urbp, *furbp, *lurbp;
+       struct uhci_td *lltd;
+
+       eurbp = eurb->hcpriv;
+       urbp = urb->hcpriv;
+
+       /* Find the first URB in the queue */
+       furbp = eurbp;
+       if (eurbp->queued) {
+               list_for_each_entry(furbp, &eurbp->queue_list, queue_list)
+                       if (!furbp->queued)
+                               break;
+       }
+
+       lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list);
+
+       lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
+
+       /* Control transfers always start with toggle 0 */
+       if (!usb_pipecontrol(urb->pipe))
+               usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+                               usb_pipeout(urb->pipe),
+                               uhci_fixup_toggle(urb,
+                                       uhci_toggle(td_token(lltd)) ^ 1));
+
+       /* All qh's in the queue need to link to the next queue */
+       urbp->qh->link = eurbp->qh->link;
+
+       wmb();                  /* Make sure we flush everything */
+
+       lltd->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH;
+
+       list_add_tail(&urbp->queue_list, &furbp->queue_list);
+
+       urbp->queued = 1;
+}
+
+static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb)
+{
+       struct urb_priv *urbp, *nurbp, *purbp, *turbp;
+       struct uhci_td *pltd;
+       unsigned int toggle;
+
+       urbp = urb->hcpriv;
+
+       if (list_empty(&urbp->queue_list))
+               return;
+
+       nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list);
+
+       /*
+        * Fix up the toggle for the following URBs in the queue.
+        * Only needed for bulk and interrupt: control and isochronous
+        * endpoints don't propagate toggles between messages.
+        */
+       if (usb_pipebulk(urb->pipe) || usb_pipeint(urb->pipe)) {
+               if (!urbp->queued)
+                       /* We just set the toggle in uhci_unlink_generic */
+                       toggle = usb_gettoggle(urb->dev,
+                                       usb_pipeendpoint(urb->pipe),
+                                       usb_pipeout(urb->pipe));
+               else {
+                       /* If we're in the middle of the queue, grab the */
+                       /* toggle from the TD previous to us */
+                       purbp = list_entry(urbp->queue_list.prev,
+                                       struct urb_priv, queue_list);
+                       pltd = list_entry(purbp->td_list.prev,
+                                       struct uhci_td, list);
+                       toggle = uhci_toggle(td_token(pltd)) ^ 1;
+               }
+
+               list_for_each_entry(turbp, &urbp->queue_list, queue_list) {
+                       if (!turbp->queued)
+                               break;
+                       toggle = uhci_fixup_toggle(turbp->urb, toggle);
+               }
+
+               usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+                               usb_pipeout(urb->pipe), toggle);
+       }
+
+       if (urbp->queued) {
+               /* We're somewhere in the middle (or end).  The case where
+                * we're at the head is handled in uhci_remove_qh(). */
+               purbp = list_entry(urbp->queue_list.prev, struct urb_priv,
+                               queue_list);
+
+               pltd = list_entry(purbp->td_list.prev, struct uhci_td, list);
+               if (nurbp->queued)
+                       pltd->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH;
+               else
+                       /* The next URB happens to be the beginning, so */
+                       /*  we're the last, end the chain */
+                       pltd->link = UHCI_PTR_TERM;
+       }
+
+       /* urbp->queue_list is handled in uhci_remove_qh() */
+}
+
+static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
+{
+       struct urb_priv *urbp;
+
+       urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC);
+       if (!urbp)
+               return NULL;
+
+       memset((void *)urbp, 0, sizeof(*urbp));
+
+       urbp->inserttime = jiffies;
+       urbp->fsbrtime = jiffies;
+       urbp->urb = urb;
+       
+       INIT_LIST_HEAD(&urbp->td_list);
+       INIT_LIST_HEAD(&urbp->queue_list);
+       INIT_LIST_HEAD(&urbp->urb_list);
+
+       list_add_tail(&urbp->urb_list, &uhci->urb_list);
+
+       urb->hcpriv = urbp;
+
+       return urbp;
+}
+
+static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td)
+{
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+       td->urb = urb;
+
+       list_add_tail(&td->list, &urbp->td_list);
+}
+
+static void uhci_remove_td_from_urb(struct uhci_td *td)
+{
+       if (list_empty(&td->list))
+               return;
+
+       list_del_init(&td->list);
+
+       td->urb = NULL;
+}
+
+static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb)
+{
+       struct uhci_td *td, *tmp;
+       struct urb_priv *urbp;
+
+       urbp = (struct urb_priv *)urb->hcpriv;
+       if (!urbp)
+               return;
+
+       if (!list_empty(&urbp->urb_list))
+               dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list "
+                               "or uhci->remove_list!\n", urb);
+
+       uhci_get_current_frame_number(uhci);
+       if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age) {
+               uhci_free_pending_tds(uhci);
+               uhci->td_remove_age = uhci->frame_number;
+       }
+
+       /* Check to see if the remove list is empty. Set the IOC bit */
+       /* to force an interrupt so we can remove the TD's*/
+       if (list_empty(&uhci->td_remove_list))
+               uhci_set_next_interrupt(uhci);
+
+       list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
+               uhci_remove_td_from_urb(td);
+               uhci_remove_td(uhci, td);
+               list_add(&td->remove_list, &uhci->td_remove_list);
+       }
+
+       urb->hcpriv = NULL;
+       kmem_cache_free(uhci_up_cachep, urbp);
+}
+
+static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+{
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+       if ((!(urb->transfer_flags & URB_NO_FSBR)) && !urbp->fsbr) {
+               urbp->fsbr = 1;
+               if (!uhci->fsbr++ && !uhci->fsbrtimeout)
+                       uhci->skel_term_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
+       }
+}
+
+static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+{
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+
+       if ((!(urb->transfer_flags & URB_NO_FSBR)) && urbp->fsbr) {
+               urbp->fsbr = 0;
+               if (!--uhci->fsbr)
+                       uhci->fsbrtimeout = jiffies + FSBR_DELAY;
+       }
+}
+
+/*
+ * Map status to standard result codes
+ *
+ * <status> is (td_status(td) & 0xF60000), a.k.a.
+ * uhci_status_bits(td_status(td)).
+ * Note: <status> does not include the TD_CTRL_NAK bit.
+ * <dir_out> is True for output TDs and False for input TDs.
+ */
+static int uhci_map_status(int status, int dir_out)
+{
+       if (!status)
+               return 0;
+       if (status & TD_CTRL_BITSTUFF)                  /* Bitstuff error */
+               return -EPROTO;
+       if (status & TD_CTRL_CRCTIMEO) {                /* CRC/Timeout */
+               if (dir_out)
+                       return -EPROTO;
+               else
+                       return -EILSEQ;
+       }
+       if (status & TD_CTRL_BABBLE)                    /* Babble */
+               return -EOVERFLOW;
+       if (status & TD_CTRL_DBUFERR)                   /* Buffer error */
+               return -ENOSR;
+       if (status & TD_CTRL_STALLED)                   /* Stalled */
+               return -EPIPE;
+       WARN_ON(status & TD_CTRL_ACTIVE);               /* Active */
+       return 0;
+}
+
+/*
+ * Control transfers
+ */
+static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
+{
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       struct uhci_td *td;
+       struct uhci_qh *qh, *skelqh;
+       unsigned long destination, status;
+       int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+       int len = urb->transfer_buffer_length;
+       dma_addr_t data = urb->transfer_dma;
+
+       /* The "pipe" thing contains the destination in bits 8--18 */
+       destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP;
+
+       /* 3 errors */
+       status = TD_CTRL_ACTIVE | uhci_maxerr(3);
+       if (urb->dev->speed == USB_SPEED_LOW)
+               status |= TD_CTRL_LS;
+
+       /*
+        * Build the TD for the control request setup packet
+        */
+       td = uhci_alloc_td(uhci, urb->dev);
+       if (!td)
+               return -ENOMEM;
+
+       uhci_add_td_to_urb(urb, td);
+       uhci_fill_td(td, status, destination | uhci_explen(7),
+               urb->setup_dma);
+
+       /*
+        * If direction is "send", change the packet ID from SETUP (0x2D)
+        * to OUT (0xE1).  Else change it from SETUP to IN (0x69) and
+        * set Short Packet Detect (SPD) for all data packets.
+        */
+       if (usb_pipeout(urb->pipe))
+               destination ^= (USB_PID_SETUP ^ USB_PID_OUT);
+       else {
+               destination ^= (USB_PID_SETUP ^ USB_PID_IN);
+               status |= TD_CTRL_SPD;
+       }
+
+       /*
+        * Build the DATA TD's
+        */
+       while (len > 0) {
+               int pktsze = len;
+
+               if (pktsze > maxsze)
+                       pktsze = maxsze;
+
+               td = uhci_alloc_td(uhci, urb->dev);
+               if (!td)
+                       return -ENOMEM;
+
+               /* Alternate Data0/1 (start with Data1) */
+               destination ^= TD_TOKEN_TOGGLE;
+       
+               uhci_add_td_to_urb(urb, td);
+               uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1),
+                       data);
+
+               data += pktsze;
+               len -= pktsze;
+       }
+
+       /*
+        * Build the final TD for control status 
+        */
+       td = uhci_alloc_td(uhci, urb->dev);
+       if (!td)
+               return -ENOMEM;
+
+       /*
+        * It's IN if the pipe is an output pipe or we're not expecting
+        * data back.
+        */
+       destination &= ~TD_TOKEN_PID_MASK;
+       if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length)
+               destination |= USB_PID_IN;
+       else
+               destination |= USB_PID_OUT;
+
+       destination |= TD_TOKEN_TOGGLE;         /* End in Data1 */
+
+       status &= ~TD_CTRL_SPD;
+
+       uhci_add_td_to_urb(urb, td);
+       uhci_fill_td(td, status | TD_CTRL_IOC,
+               destination | uhci_explen(UHCI_NULL_DATA_SIZE), 0);
+
+       qh = uhci_alloc_qh(uhci, urb->dev);
+       if (!qh)
+               return -ENOMEM;
+
+       urbp->qh = qh;
+       qh->urbp = urbp;
+
+       uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
+
+       /* Low-speed transfers get a different queue, and won't hog the bus.
+        * Also, some devices enumerate better without FSBR; the easiest way
+        * to do that is to put URBs on the low-speed queue while the device
+        * is in the DEFAULT state. */
+       if (urb->dev->speed == USB_SPEED_LOW ||
+                       urb->dev->state == USB_STATE_DEFAULT)
+               skelqh = uhci->skel_ls_control_qh;
+       else {
+               skelqh = uhci->skel_fs_control_qh;
+               uhci_inc_fsbr(uhci, urb);
+       }
+
+       if (eurb)
+               uhci_append_queued_urb(uhci, eurb, urb);
+       else
+               uhci_insert_qh(uhci, skelqh, urb);
+
+       return -EINPROGRESS;
+}
+
+/*
+ * If control-IN transfer was short, the status packet wasn't sent.
+ * This routine changes the element pointer in the QH to point at the
+ * status TD.  It's safe to do this even while the QH is live, because
+ * the hardware only updates the element pointer following a successful
+ * transfer.  The inactive TD for the short packet won't cause an update,
+ * so the pointer won't get overwritten.  The next time the controller
+ * sees this QH, it will send the status packet.
+ */
+static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb)
+{
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       struct uhci_td *td;
+
+       urbp->short_control_packet = 1;
+
+       td = list_entry(urbp->td_list.prev, struct uhci_td, list);
+       urbp->qh->element = cpu_to_le32(td->dma_handle);
+
+       return -EINPROGRESS;
+}
+
+
+static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb)
+{
+       struct list_head *tmp, *head;
+       struct urb_priv *urbp = urb->hcpriv;
+       struct uhci_td *td;
+       unsigned int status;
+       int ret = 0;
+
+       if (list_empty(&urbp->td_list))
+               return -EINVAL;
+
+       head = &urbp->td_list;
+
+       if (urbp->short_control_packet) {
+               tmp = head->prev;
+               goto status_stage;
+       }
+
+       tmp = head->next;
+       td = list_entry(tmp, struct uhci_td, list);
+
+       /* The first TD is the SETUP stage, check the status, but skip */
+       /*  the count */
+       status = uhci_status_bits(td_status(td));
+       if (status & TD_CTRL_ACTIVE)
+               return -EINPROGRESS;
+
+       if (status)
+               goto td_error;
+
+       urb->actual_length = 0;
+
+       /* The rest of the TD's (but the last) are data */
+       tmp = tmp->next;
+       while (tmp != head && tmp->next != head) {
+               unsigned int ctrlstat;
+
+               td = list_entry(tmp, struct uhci_td, list);
+               tmp = tmp->next;
+
+               ctrlstat = td_status(td);
+               status = uhci_status_bits(ctrlstat);
+               if (status & TD_CTRL_ACTIVE)
+                       return -EINPROGRESS;
+
+               urb->actual_length += uhci_actual_length(ctrlstat);
+
+               if (status)
+                       goto td_error;
+
+               /* Check to see if we received a short packet */
+               if (uhci_actual_length(ctrlstat) <
+                               uhci_expected_length(td_token(td))) {
+                       if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+                               ret = -EREMOTEIO;
+                               goto err;
+                       }
+
+                       if (uhci_packetid(td_token(td)) == USB_PID_IN)
+                               return usb_control_retrigger_status(uhci, urb);
+                       else
+                               return 0;
+               }
+       }
+
+status_stage:
+       td = list_entry(tmp, struct uhci_td, list);
+
+       /* Control status stage */
+       status = td_status(td);
+
+#ifdef I_HAVE_BUGGY_APC_BACKUPS
+       /* APC BackUPS Pro kludge */
+       /* It tries to send all of the descriptor instead of the amount */
+       /*  we requested */
+       if (status & TD_CTRL_IOC &&     /* IOC is masked out by uhci_status_bits */
+           status & TD_CTRL_ACTIVE &&
+           status & TD_CTRL_NAK)
+               return 0;
+#endif
+
+       status = uhci_status_bits(status);
+       if (status & TD_CTRL_ACTIVE)
+               return -EINPROGRESS;
+
+       if (status)
+               goto td_error;
+
+       return 0;
+
+td_error:
+       ret = uhci_map_status(status, uhci_packetout(td_token(td)));
+
+err:
+       if ((debug == 1 && ret != -EPIPE) || debug > 1) {
+               /* Some debugging code */
+               dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n",
+                               __FUNCTION__, status);
+
+               if (errbuf) {
+                       /* Print the chain for debugging purposes */
+                       uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
+
+                       lprintk(errbuf);
+               }
+       }
+
+       return ret;
+}
+
+/*
+ * Common submit for bulk and interrupt
+ */
+static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb, struct uhci_qh *skelqh)
+{
+       struct uhci_td *td;
+       struct uhci_qh *qh;
+       unsigned long destination, status;
+       int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+       int len = urb->transfer_buffer_length;
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       dma_addr_t data = urb->transfer_dma;
+
+       if (len < 0)
+               return -EINVAL;
+
+       /* The "pipe" thing contains the destination in bits 8--18 */
+       destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+       status = uhci_maxerr(3) | TD_CTRL_ACTIVE;
+       if (urb->dev->speed == USB_SPEED_LOW)
+               status |= TD_CTRL_LS;
+       if (usb_pipein(urb->pipe))
+               status |= TD_CTRL_SPD;
+
+       /*
+        * Build the DATA TD's
+        */
+       do {    /* Allow zero length packets */
+               int pktsze = maxsze;
+
+               if (pktsze >= len) {
+                       pktsze = len;
+                       if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+                               status &= ~TD_CTRL_SPD;
+               }
+
+               td = uhci_alloc_td(uhci, urb->dev);
+               if (!td)
+                       return -ENOMEM;
+
+               uhci_add_td_to_urb(urb, td);
+               uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1) |
+                       (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+                        usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
+                       data);
+
+               data += pktsze;
+               len -= maxsze;
+
+               usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+                       usb_pipeout(urb->pipe));
+       } while (len > 0);
+
+       /*
+        * URB_ZERO_PACKET means adding a 0-length packet, if direction
+        * is OUT and the transfer_length was an exact multiple of maxsze,
+        * hence (len = transfer_length - N * maxsze) == 0
+        * however, if transfer_length == 0, the zero packet was already
+        * prepared above.
+        */
+       if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) &&
+           !len && urb->transfer_buffer_length) {
+               td = uhci_alloc_td(uhci, urb->dev);
+               if (!td)
+                       return -ENOMEM;
+
+               uhci_add_td_to_urb(urb, td);
+               uhci_fill_td(td, status, destination | uhci_explen(UHCI_NULL_DATA_SIZE) |
+                       (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+                        usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
+                       data);
+
+               usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+                       usb_pipeout(urb->pipe));
+       }
+
+       /* Set the interrupt-on-completion flag on the last packet.
+        * A more-or-less typical 4 KB URB (= size of one memory page)
+        * will require about 3 ms to transfer; that's a little on the
+        * fast side but not enough to justify delaying an interrupt
+        * more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT
+        * flag setting. */
+       td->status |= cpu_to_le32(TD_CTRL_IOC);
+
+       qh = uhci_alloc_qh(uhci, urb->dev);
+       if (!qh)
+               return -ENOMEM;
+
+       urbp->qh = qh;
+       qh->urbp = urbp;
+
+       /* Always breadth first */
+       uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH);
+
+       if (eurb)
+               uhci_append_queued_urb(uhci, eurb, urb);
+       else
+               uhci_insert_qh(uhci, skelqh, urb);
+
+       return -EINPROGRESS;
+}
+
+/*
+ * Common result for bulk and interrupt
+ */
+static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
+{
+       struct urb_priv *urbp = urb->hcpriv;
+       struct uhci_td *td;
+       unsigned int status = 0;
+       int ret = 0;
+
+       urb->actual_length = 0;
+
+       list_for_each_entry(td, &urbp->td_list, list) {
+               unsigned int ctrlstat = td_status(td);
+
+               status = uhci_status_bits(ctrlstat);
+               if (status & TD_CTRL_ACTIVE)
+                       return -EINPROGRESS;
+
+               urb->actual_length += uhci_actual_length(ctrlstat);
+
+               if (status)
+                       goto td_error;
+
+               if (uhci_actual_length(ctrlstat) <
+                               uhci_expected_length(td_token(td))) {
+                       if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+                               ret = -EREMOTEIO;
+                               goto err;
+                       } else
+                               return 0;
+               }
+       }
+
+       return 0;
+
+td_error:
+       ret = uhci_map_status(status, uhci_packetout(td_token(td)));
+
+err:
+       /* 
+        * Enable this chunk of code if you want to see some more debugging.
+        * But be careful, it has the tendancy to starve out khubd and prevent
+        * disconnects from happening successfully if you have a slow debug
+        * log interface (like a serial console.
+        */
+#if 0
+       if ((debug == 1 && ret != -EPIPE) || debug > 1) {
+               /* Some debugging code */
+               dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n",
+                               __FUNCTION__, status);
+
+               if (errbuf) {
+                       /* Print the chain for debugging purposes */
+                       uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
+
+                       lprintk(errbuf);
+               }
+       }
+#endif
+       return ret;
+}
+
+static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
+{
+       int ret;
+
+       /* Can't have low-speed bulk transfers */
+       if (urb->dev->speed == USB_SPEED_LOW)
+               return -EINVAL;
+
+       ret = uhci_submit_common(uhci, urb, eurb, uhci->skel_bulk_qh);
+       if (ret == -EINPROGRESS)
+               uhci_inc_fsbr(uhci, urb);
+
+       return ret;
+}
+
+static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
+{
+       /* USB 1.1 interrupt transfers only involve one packet per interval;
+        * that's the uhci_submit_common() "breadth first" policy.  Drivers
+        * can submit urbs of any length, but longer ones might need many
+        * intervals to complete.
+        */
+       return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]);
+}
+
+/*
+ * Isochronous transfers
+ */
+static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end)
+{
+       struct urb *last_urb = NULL;
+       struct urb_priv *up;
+       int ret = 0;
+
+       list_for_each_entry(up, &uhci->urb_list, urb_list) {
+               struct urb *u = up->urb;
+
+               /* look for pending URB's with identical pipe handle */
+               if ((urb->pipe == u->pipe) && (urb->dev == u->dev) &&
+                   (u->status == -EINPROGRESS) && (u != urb)) {
+                       if (!last_urb)
+                               *start = u->start_frame;
+                       last_urb = u;
+               }
+       }
+
+       if (last_urb) {
+               *end = (last_urb->start_frame + last_urb->number_of_packets *
+                               last_urb->interval) & (UHCI_NUMFRAMES-1);
+               ret = 0;
+       } else
+               ret = -1;       /* no previous urb found */
+
+       return ret;
+}
+
+static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb)
+{
+       int limits;
+       unsigned int start = 0, end = 0;
+
+       if (urb->number_of_packets > 900)       /* 900? Why? */
+               return -EFBIG;
+
+       limits = isochronous_find_limits(uhci, urb, &start, &end);
+
+       if (urb->transfer_flags & URB_ISO_ASAP) {
+               if (limits) {
+                       uhci_get_current_frame_number(uhci);
+                       urb->start_frame = (uhci->frame_number + 10)
+                                       & (UHCI_NUMFRAMES - 1);
+               } else
+                       urb->start_frame = end;
+       } else {
+               urb->start_frame &= (UHCI_NUMFRAMES - 1);
+               /* FIXME: Sanity check */
+       }
+
+       return 0;
+}
+
+/*
+ * Isochronous transfers
+ */
+static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb)
+{
+       struct uhci_td *td;
+       int i, ret, frame;
+       int status, destination;
+
+       status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
+       destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
+       ret = isochronous_find_start(uhci, urb);
+       if (ret)
+               return ret;
+
+       frame = urb->start_frame;
+       for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) {
+               if (!urb->iso_frame_desc[i].length)
+                       continue;
+
+               td = uhci_alloc_td(uhci, urb->dev);
+               if (!td)
+                       return -ENOMEM;
+
+               uhci_add_td_to_urb(urb, td);
+               uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length - 1),
+                       urb->transfer_dma + urb->iso_frame_desc[i].offset);
+
+               if (i + 1 >= urb->number_of_packets)
+                       td->status |= cpu_to_le32(TD_CTRL_IOC);
+
+               uhci_insert_td_frame_list(uhci, td, frame);
+       }
+
+       return -EINPROGRESS;
+}
+
+static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
+{
+       struct uhci_td *td;
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       int status;
+       int i, ret = 0;
+
+       urb->actual_length = 0;
+
+       i = 0;
+       list_for_each_entry(td, &urbp->td_list, list) {
+               int actlength;
+               unsigned int ctrlstat = td_status(td);
+
+               if (ctrlstat & TD_CTRL_ACTIVE)
+                       return -EINPROGRESS;
+
+               actlength = uhci_actual_length(ctrlstat);
+               urb->iso_frame_desc[i].actual_length = actlength;
+               urb->actual_length += actlength;
+
+               status = uhci_map_status(uhci_status_bits(ctrlstat),
+                               usb_pipeout(urb->pipe));
+               urb->iso_frame_desc[i].status = status;
+               if (status) {
+                       urb->error_count++;
+                       ret = status;
+               }
+
+               i++;
+       }
+
+       return ret;
+}
+
+static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb)
+{
+       struct urb_priv *up;
+
+       /* We don't match Isoc transfers since they are special */
+       if (usb_pipeisoc(urb->pipe))
+               return NULL;
+
+       list_for_each_entry(up, &uhci->urb_list, urb_list) {
+               struct urb *u = up->urb;
+
+               if (u->dev == urb->dev && u->status == -EINPROGRESS) {
+                       /* For control, ignore the direction */
+                       if (usb_pipecontrol(urb->pipe) &&
+                           (u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN))
+                               return u;
+                       else if (u->pipe == urb->pipe)
+                               return u;
+               }
+       }
+
+       return NULL;
+}
+
+static int uhci_urb_enqueue(struct usb_hcd *hcd,
+               struct usb_host_endpoint *ep,
+               struct urb *urb, int mem_flags)
+{
+       int ret;
+       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+       unsigned long flags;
+       struct urb *eurb;
+       int bustime;
+
+       spin_lock_irqsave(&uhci->lock, flags);
+
+       ret = urb->status;
+       if (ret != -EINPROGRESS)                /* URB already unlinked! */
+               goto out;
+
+       eurb = uhci_find_urb_ep(uhci, urb);
+
+       if (!uhci_alloc_urb_priv(uhci, urb)) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       switch (usb_pipetype(urb->pipe)) {
+       case PIPE_CONTROL:
+               ret = uhci_submit_control(uhci, urb, eurb);
+               break;
+       case PIPE_INTERRUPT:
+               if (!eurb) {
+                       bustime = usb_check_bandwidth(urb->dev, urb);
+                       if (bustime < 0)
+                               ret = bustime;
+                       else {
+                               ret = uhci_submit_interrupt(uhci, urb, eurb);
+                               if (ret == -EINPROGRESS)
+                                       usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+                       }
+               } else {        /* inherit from parent */
+                       urb->bandwidth = eurb->bandwidth;
+                       ret = uhci_submit_interrupt(uhci, urb, eurb);
+               }
+               break;
+       case PIPE_BULK:
+               ret = uhci_submit_bulk(uhci, urb, eurb);
+               break;
+       case PIPE_ISOCHRONOUS:
+               bustime = usb_check_bandwidth(urb->dev, urb);
+               if (bustime < 0) {
+                       ret = bustime;
+                       break;
+               }
+
+               ret = uhci_submit_isochronous(uhci, urb);
+               if (ret == -EINPROGRESS)
+                       usb_claim_bandwidth(urb->dev, urb, bustime, 1);
+               break;
+       }
+
+       if (ret != -EINPROGRESS) {
+               /* Submit failed, so delete it from the urb_list */
+               struct urb_priv *urbp = urb->hcpriv;
+
+               list_del_init(&urbp->urb_list);
+               uhci_destroy_urb_priv(uhci, urb);
+       } else
+               ret = 0;
+
+out:
+       spin_unlock_irqrestore(&uhci->lock, flags);
+       return ret;
+}
+
+/*
+ * Return the result of a transfer
+ */
+static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb)
+{
+       int ret = -EINPROGRESS;
+       struct urb_priv *urbp;
+
+       spin_lock(&urb->lock);
+
+       urbp = (struct urb_priv *)urb->hcpriv;
+
+       if (urb->status != -EINPROGRESS)        /* URB already dequeued */
+               goto out;
+
+       switch (usb_pipetype(urb->pipe)) {
+       case PIPE_CONTROL:
+               ret = uhci_result_control(uhci, urb);
+               break;
+       case PIPE_BULK:
+       case PIPE_INTERRUPT:
+               ret = uhci_result_common(uhci, urb);
+               break;
+       case PIPE_ISOCHRONOUS:
+               ret = uhci_result_isochronous(uhci, urb);
+               break;
+       }
+
+       if (ret == -EINPROGRESS)
+               goto out;
+       urb->status = ret;
+
+       switch (usb_pipetype(urb->pipe)) {
+       case PIPE_CONTROL:
+       case PIPE_BULK:
+       case PIPE_ISOCHRONOUS:
+               /* Release bandwidth for Interrupt or Isoc. transfers */
+               if (urb->bandwidth)
+                       usb_release_bandwidth(urb->dev, urb, 1);
+               uhci_unlink_generic(uhci, urb);
+               break;
+       case PIPE_INTERRUPT:
+               /* Release bandwidth for Interrupt or Isoc. transfers */
+               /* Make sure we don't release if we have a queued URB */
+               if (list_empty(&urbp->queue_list) && urb->bandwidth)
+                       usb_release_bandwidth(urb->dev, urb, 0);
+               else
+                       /* bandwidth was passed on to queued URB, */
+                       /* so don't let usb_unlink_urb() release it */
+                       urb->bandwidth = 0;
+               uhci_unlink_generic(uhci, urb);
+               break;
+       default:
+               dev_info(uhci_dev(uhci), "%s: unknown pipe type %d "
+                               "for urb %p\n",
+                               __FUNCTION__, usb_pipetype(urb->pipe), urb);
+       }
+
+       /* Move it from uhci->urb_list to uhci->complete_list */
+       uhci_moveto_complete(uhci, urbp);
+
+out:
+       spin_unlock(&urb->lock);
+}
+
+static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb)
+{
+       struct list_head *head;
+       struct uhci_td *td;
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       int prevactive = 0;
+
+       uhci_dec_fsbr(uhci, urb);       /* Safe since it checks */
+
+       /*
+        * Now we need to find out what the last successful toggle was
+        * so we can update the local data toggle for the next transfer
+        *
+        * There are 2 ways the last successful completed TD is found:
+        *
+        * 1) The TD is NOT active and the actual length < expected length
+        * 2) The TD is NOT active and it's the last TD in the chain
+        *
+        * and a third way the first uncompleted TD is found:
+        *
+        * 3) The TD is active and the previous TD is NOT active
+        *
+        * Control and Isochronous ignore the toggle, so this is safe
+        * for all types
+        *
+        * FIXME: The toggle fixups won't be 100% reliable until we
+        * change over to using a single queue for each endpoint and
+        * stop the queue before unlinking.
+        */
+       head = &urbp->td_list;
+       list_for_each_entry(td, head, list) {
+               unsigned int ctrlstat = td_status(td);
+
+               if (!(ctrlstat & TD_CTRL_ACTIVE) &&
+                               (uhci_actual_length(ctrlstat) <
+                                uhci_expected_length(td_token(td)) ||
+                               td->list.next == head))
+                       usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
+                               uhci_packetout(td_token(td)),
+                               uhci_toggle(td_token(td)) ^ 1);
+               else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive)
+                       usb_settoggle(urb->dev, uhci_endpoint(td_token(td)),
+                               uhci_packetout(td_token(td)),
+                               uhci_toggle(td_token(td)));
+
+               prevactive = ctrlstat & TD_CTRL_ACTIVE;
+       }
+
+       uhci_delete_queued_urb(uhci, urb);
+
+       /* The interrupt loop will reclaim the QH's */
+       uhci_remove_qh(uhci, urbp->qh);
+       urbp->qh = NULL;
+}
+
+static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+       unsigned long flags;
+       struct urb_priv *urbp;
+
+       spin_lock_irqsave(&uhci->lock, flags);
+       urbp = urb->hcpriv;
+       if (!urbp)                      /* URB was never linked! */
+               goto done;
+       list_del_init(&urbp->urb_list);
+
+       uhci_unlink_generic(uhci, urb);
+
+       uhci_get_current_frame_number(uhci);
+       if (uhci->frame_number + uhci->is_stopped != uhci->urb_remove_age) {
+               uhci_remove_pending_urbps(uhci);
+               uhci->urb_remove_age = uhci->frame_number;
+       }
+
+       /* If we're the first, set the next interrupt bit */
+       if (list_empty(&uhci->urb_remove_list))
+               uhci_set_next_interrupt(uhci);
+       list_add_tail(&urbp->urb_list, &uhci->urb_remove_list);
+
+done:
+       spin_unlock_irqrestore(&uhci->lock, flags);
+       return 0;
+}
+
+static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb)
+{
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       struct list_head *head;
+       struct uhci_td *td;
+       int count = 0;
+
+       uhci_dec_fsbr(uhci, urb);
+
+       urbp->fsbr_timeout = 1;
+
+       /*
+        * Ideally we would want to fix qh->element as well, but it's
+        * read/write by the HC, so that can introduce a race. It's not
+        * really worth the hassle
+        */
+
+       head = &urbp->td_list;
+       list_for_each_entry(td, head, list) {
+               /*
+                * Make sure we don't do the last one (since it'll have the
+                * TERM bit set) as well as we skip every so many TD's to
+                * make sure it doesn't hog the bandwidth
+                */
+               if (td->list.next != head && (count % DEPTH_INTERVAL) ==
+                               (DEPTH_INTERVAL - 1))
+                       td->link |= UHCI_PTR_DEPTH;
+
+               count++;
+       }
+
+       return 0;
+}
+
+static void uhci_free_pending_qhs(struct uhci_hcd *uhci)
+{
+       struct uhci_qh *qh, *tmp;
+
+       list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) {
+               list_del_init(&qh->remove_list);
+
+               uhci_free_qh(uhci, qh);
+       }
+}
+
+static void uhci_free_pending_tds(struct uhci_hcd *uhci)
+{
+       struct uhci_td *td, *tmp;
+
+       list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) {
+               list_del_init(&td->remove_list);
+
+               uhci_free_td(uhci, td);
+       }
+}
+
+static void
+uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
+__releases(uhci->lock)
+__acquires(uhci->lock)
+{
+       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+       uhci_destroy_urb_priv(uhci, urb);
+
+       spin_unlock(&uhci->lock);
+       usb_hcd_giveback_urb(hcd, urb, regs);
+       spin_lock(&uhci->lock);
+}
+
+static void uhci_finish_completion(struct uhci_hcd *uhci, struct pt_regs *regs)
+{
+       struct urb_priv *urbp, *tmp;
+
+       list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) {
+               struct urb *urb = urbp->urb;
+
+               list_del_init(&urbp->urb_list);
+               uhci_finish_urb(uhci_to_hcd(uhci), urb, regs);
+       }
+}
+
+static void uhci_remove_pending_urbps(struct uhci_hcd *uhci)
+{
+
+       /* Splice the urb_remove_list onto the end of the complete_list */
+       list_splice_init(&uhci->urb_remove_list, uhci->complete_list.prev);
+}
+
+/* Process events in the schedule, but only in one thread at a time */
+static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
+{
+       struct urb_priv *urbp, *tmp;
+
+       /* Don't allow re-entrant calls */
+       if (uhci->scan_in_progress) {
+               uhci->need_rescan = 1;
+               return;
+       }
+       uhci->scan_in_progress = 1;
+ rescan:
+       uhci->need_rescan = 0;
+
+       uhci_get_current_frame_number(uhci);
+
+       if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age)
+               uhci_free_pending_qhs(uhci);
+       if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age)
+               uhci_free_pending_tds(uhci);
+       if (uhci->frame_number + uhci->is_stopped != uhci->urb_remove_age)
+               uhci_remove_pending_urbps(uhci);
+
+       /* Walk the list of pending URBs to see which ones completed
+        * (must be _safe because uhci_transfer_result() dequeues URBs) */
+       list_for_each_entry_safe(urbp, tmp, &uhci->urb_list, urb_list) {
+               struct urb *urb = urbp->urb;
+
+               /* Checks the status and does all of the magic necessary */
+               uhci_transfer_result(uhci, urb);
+       }
+       uhci_finish_completion(uhci, regs);
+
+       /* If the controller is stopped, we can finish these off right now */
+       if (uhci->is_stopped) {
+               uhci_free_pending_qhs(uhci);
+               uhci_free_pending_tds(uhci);
+               uhci_remove_pending_urbps(uhci);
+       }
+
+       if (uhci->need_rescan)
+               goto rescan;
+       uhci->scan_in_progress = 0;
+
+       if (list_empty(&uhci->urb_remove_list) &&
+           list_empty(&uhci->td_remove_list) &&
+           list_empty(&uhci->qh_remove_list))
+               uhci_clear_next_interrupt(uhci);
+       else
+               uhci_set_next_interrupt(uhci);
+
+       /* Wake up anyone waiting for an URB to complete */
+       wake_up_all(&uhci->waitqh);
+}
diff --git a/drivers/usb/media/pwc/philips.txt b/drivers/usb/media/pwc/philips.txt
new file mode 100644 (file)
index 0000000..04a640d
--- /dev/null
@@ -0,0 +1,236 @@
+This file contains some additional information for the Philips and OEM webcams.
+E-mail: webcam@smcc.demon.nl                        Last updated: 2004-01-19
+Site: http://www.smcc.demon.nl/webcam/
+
+As of this moment, the following cameras are supported:
+ * Philips PCA645
+ * Philips PCA646
+ * Philips PCVC675
+ * Philips PCVC680
+ * Philips PCVC690
+ * Philips PCVC720/40
+ * Philips PCVC730
+ * Philips PCVC740
+ * Philips PCVC750
+ * Askey VC010
+ * Creative Labs Webcam 5
+ * Creative Labs Webcam Pro Ex
+ * Logitech QuickCam 3000 Pro
+ * Logitech QuickCam 4000 Pro
+ * Logitech QuickCam Notebook Pro
+ * Logitech QuickCam Zoom
+ * Logitech QuickCam Orbit
+ * Logitech QuickCam Sphere
+ * Samsung MPC-C10
+ * Samsung MPC-C30
+ * Sotec Afina Eye
+ * AME CU-001
+ * Visionite VCS-UM100
+ * Visionite VCS-UC300
+
+The main webpage for the Philips driver is at the address above. It contains
+a lot of extra information, a FAQ, and the binary plugin 'PWCX'. This plugin
+contains decompression routines that allow you to use higher image sizes and
+framerates; in addition the webcam uses less bandwidth on the USB bus (handy
+if you want to run more than 1 camera simultaneously). These routines fall
+under a NDA, and may therefor not be distributed as source; however, its use
+is completely optional.
+
+You can build this code either into your kernel, or as a module. I recommend
+the latter, since it makes troubleshooting a lot easier. The built-in
+microphone is supported through the USB Audio class.
+
+When you load the module you can set some default settings for the
+camera; some programs depend on a particular image-size or -format and
+don't know how to set it properly in the driver. The options are:
+
+size
+   Can be one of 'sqcif', 'qsif', 'qcif', 'sif', 'cif' or
+   'vga', for an image size of resp. 128x96, 160x120, 176x144,
+   320x240, 352x288 and 640x480 (of course, only for those cameras that 
+   support these resolutions).
+
+fps
+   Specifies the desired framerate. Is an integer in the range of 4-30.
+
+fbufs
+   This paramter specifies the number of internal buffers to use for storing 
+   frames from the cam. This will help if the process that reads images from 
+   the cam is a bit slow or momentarely busy. However, on slow machines it 
+   only introduces lag, so choose carefully. The default is 3, which is 
+   reasonable. You can set it between 2 and 5.
+
+mbufs
+   This is an integer between 1 and 10. It will tell the module the number of
+   buffers to reserve for mmap(), VIDIOCCGMBUF, VIDIOCMCAPTURE and friends.
+   The default is 2, which is adequate for most applications (double
+   buffering).
+      
+   Should you experience a lot of 'Dumping frame...' messages during
+   grabbing with a tool that uses mmap(), you might want to increase if. 
+   However, it doesn't really buffer images, it just gives you a bit more
+   slack when your program is behind. But you need a multi-threaded or
+   forked program to really take advantage of these buffers.
+
+   The absolute maximum is 10, but don't set it too high!  Every buffer takes
+   up 460 KB of RAM, so unless you have a lot of memory setting this to
+   something more than 4 is an absolute waste.  This memory is only
+   allocated during open(), so nothing is wasted when the camera is not in
+   use.
+
+power_save
+   When power_save is enabled (set to 1), the module will try to shut down
+   the cam on close() and re-activate on open(). This will save power and
+   turn off the LED. Not all cameras support this though (the 645 and 646
+   don't have power saving at all), and some models don't work either (they
+   will shut down, but never wake up). Consider this experimental. By
+   default this option is disabled.
+
+compression (only useful with the plugin)
+   With this option you can control the compression factor that the camera
+   uses to squeeze the image through the USB bus. You can set the 
+   parameter between 0 and 3:
+     0 = prefer uncompressed images; if the requested mode is not available
+         in an uncompressed format, the driver will silently switch to low
+         compression.
+     1 = low compression.
+     2 = medium compression.
+     3 = high compression.
+      
+   High compression takes less bandwidth of course, but it could also
+   introduce some unwanted artefacts. The default is 2, medium compression.
+   See the FAQ on the website for an overview of which modes require
+   compression.
+
+   The compression parameter does not apply to the 645 and 646 cameras
+   and OEM models derived from those (only a few). Most cams honour this
+   parameter.
+
+leds
+   This settings takes 2 integers, that define the on/off time for the LED
+   (in milliseconds). One of the interesting things that you can do with
+   this is let the LED blink while the camera is in use. This:
+
+     leds=500,500
+      
+   will blink the LED once every second. But with:
+
+     leds=0,0
+
+   the LED never goes on, making it suitable for silent surveillance.
+
+   By default the camera's LED is on solid while in use, and turned off
+   when the camera is not used anymore.
+
+   This parameter works only with the ToUCam range of cameras (720, 730, 740,
+   750) and OEMs. For other cameras this command is silently ignored, and 
+   the LED cannot be controlled.
+
+   Finally: this parameters does not take effect UNTIL the first time you
+   open the camera device. Until then, the LED remains on.
+
+dev_hint
+   A long standing problem with USB devices is their dynamic nature: you
+   never know what device a camera gets assigned; it depends on module load
+   order, the hub configuration, the order in which devices are plugged in,
+   and the phase of the moon (i.e. it can be random). With this option you
+   can give the driver a hint as to what video device node (/dev/videoX) it
+   should use with a specific camera. This is also handy if you have two
+   cameras of the same model.
+
+   A camera is specified by its type (the number from the camera model,
+   like PCA645, PCVC750VC, etc) and optionally the serial number (visible
+   in /proc/bus/usb/devices). A hint consists of a string with the following
+   format:
+
+      [type[.serialnumber]:]node
+      
+   The square brackets mean that both the type and the serialnumber are
+   optional, but a serialnumber cannot be specified without a type (which
+   would be rather pointless). The serialnumber is separated from the type
+   by a '.'; the node number by a ':'.
+   
+   This somewhat cryptic syntax is best explained by a few examples:
+
+     dev_hint=3,5              The first detected cam gets assigned
+                               /dev/video3, the second /dev/video5. Any
+                               other cameras will get the first free 
+                               available slot (see below).
+
+     dev_hint=645:1,680:2      The PCA645 camera will get /dev/video1,
+                               and a PCVC680 /dev/video2.
+                               
+     dev_hint=645.0123:3,645.4567:0    The PCA645 camera with serialnumber 
+                                        0123 goes to /dev/video3, the same
+                                        camera model with the 4567 serial
+                                        gets /dev/video0.
+
+     dev_hint=750:1,4,5,6       The PCVC750 camera will get /dev/video1, the 
+                                next 3 Philips cams will use /dev/video4 
+                                through /dev/video6.
+
+   Some points worth knowing:
+   - Serialnumbers are case sensitive and must be written full, including 
+     leading zeroes (it's treated as a string).
+   - If a device node is already occupied, registration will fail and 
+     the webcam is not available.
+   - You can have up to 64 video devices; be sure to make enough device
+     nodes in /dev if you want to spread the numbers (this does not apply
+     to devfs). After /dev/video9 comes /dev/video10 (not /dev/videoA).
+   - If a camera does not match any dev_hint, it will simply get assigned
+     the first available device node, just as it used to be.
+
+trace
+   In order to better detect problems, it is now possible to turn on a
+   'trace' of some of the calls the module makes; it logs all items in your
+   kernel log at debug level.
+
+   The trace variable is a bitmask; each bit represents a certain feature.
+   If you want to trace something, look up the bit value(s) in the table 
+   below, add the values together and supply that to the trace variable.
+
+   Value  Value   Description                                     Default
+   (dec)  (hex)
+       1    0x1   Module initialization; this will log messages       On
+                  while loading and unloading the module
+
+       2    0x2   probe() and disconnect() traces                     On
+
+       4    0x4   Trace open() and close() calls                      Off
+
+       8    0x8   read(), mmap() and associated ioctl() calls         Off
+
+      16   0x10   Memory allocation of buffers, etc.                  Off
+
+      32   0x20   Showing underflow, overflow and Dumping frame       On
+                  messages
+
+      64   0x40   Show viewport and image sizes                       Off
+
+     128   0x80   PWCX debugging                                      Off
+
+   For example, to trace the open() & read() fuctions, sum 8 + 4 = 12,
+   so you would supply trace=12 during insmod or modprobe. If
+   you want to turn the initialization and probing tracing off, set trace=0.
+   The default value for trace is 35 (0x23).
+
+
+
+Example:
+     
+     # modprobe pwc size=cif fps=15 power_save=1
+
+The fbufs, mbufs and trace parameters are global and apply to all connected
+cameras. Each camera has its own set of buffers.
+
+size and fps only specify defaults when you open() the device; this is to
+accommodate some tools that don't set the size. You can change these
+settings after open() with the Video4Linux ioctl() calls. The default of
+defaults is QCIF size at 10 fps.
+
+The compression parameter is semiglobal; it sets the initial compression
+preference for all camera's, but this parameter can be set per camera with
+the VIDIOCPWCSCQUAL ioctl() call.
+
+All parameters are optional.
+
diff --git a/drivers/usb/media/pwc/pwc-kiara.h b/drivers/usb/media/pwc/pwc-kiara.h
new file mode 100644 (file)
index 0000000..12929ab
--- /dev/null
@@ -0,0 +1,45 @@
+/* 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
+*/
+
+/* Entries for the Kiara (730/740/750) camera */
+
+#ifndef PWC_KIARA_H
+#define PWC_KIARA_H
+
+#include "pwc-ioctl.h"
+
+struct Kiara_table_entry
+{
+       char alternate;                 /* USB alternate interface */
+       unsigned short packetsize;      /* Normal packet size */
+       unsigned short bandlength;      /* Bandlength when decompressing */
+       unsigned char mode[12];         /* precomputed mode settings for cam */
+};
+
+const extern struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4];
+const extern unsigned int KiaraRomTable[8][2][16][8];
+
+#endif
+
+
diff --git a/drivers/usb/media/pwc/pwc-misc.c b/drivers/usb/media/pwc/pwc-misc.c
new file mode 100644 (file)
index 0000000..b7a4bd3
--- /dev/null
@@ -0,0 +1,140 @@
+/* Linux driver for Philips webcam 
+   Various miscellaneous functions and tables.
+   (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 <linux/slab.h>
+
+#include "pwc.h"
+
+struct pwc_coord pwc_image_sizes[PSZ_MAX] =
+{
+       { 128,  96, 0 },
+       { 160, 120, 0 },
+       { 176, 144, 0 },
+       { 320, 240, 0 },
+       { 352, 288, 0 },
+       { 640, 480, 0 },
+};
+
+/* x,y -> PSZ_ */
+int pwc_decode_size(struct pwc_device *pdev, int width, int height)
+{
+       int i, find;
+
+       /* Make sure we don't go beyond our max size.
+           NB: we have different limits for RAW and normal modes. In case
+           you don't have the decompressor loaded or use RAW mode, 
+           the maximum viewable size is smaller.
+        */
+       if (pdev->vpalette == VIDEO_PALETTE_RAW)
+       {
+               if (width > pdev->abs_max.x || height > pdev->abs_max.y)
+               {
+                       Debug("VIDEO_PALETTE_RAW: going beyond abs_max.\n");
+                       return -1;
+                }
+       }
+       else
+       {
+               if (width > pdev->view_max.x || height > pdev->view_max.y)
+               {
+                       Debug("VIDEO_PALETTE_ not RAW: going beyond view_max.\n");
+                       return -1;
+               }
+       }
+
+       /* Find the largest size supported by the camera that fits into the
+          requested size.
+        */
+       find = -1;
+       for (i = 0; i < PSZ_MAX; i++) {
+               if (pdev->image_mask & (1 << i)) {
+                       if (pwc_image_sizes[i].x <= width && pwc_image_sizes[i].y <= height)
+                               find = i;
+               }
+       }
+       return find;
+}
+
+/* initialize variables depending on type and decompressor*/
+void pwc_construct(struct pwc_device *pdev)
+{
+       switch(pdev->type) {
+       case 645:
+       case 646:
+               pdev->view_min.x = 128;
+               pdev->view_min.y =  96;
+               pdev->view_max.x = 352;
+               pdev->view_max.y = 288;
+                pdev->abs_max.x  = 352;
+                pdev->abs_max.y  = 288;
+               pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF;
+               pdev->vcinterface = 2;
+               pdev->vendpoint = 4;
+               pdev->frame_header_size = 0;
+               pdev->frame_trailer_size = 0;
+               break;
+       case 675:
+       case 680:
+       case 690:
+               pdev->view_min.x = 128;
+               pdev->view_min.y =  96;
+               /* Anthill bug #38: PWC always reports max size, even without PWCX */
+               pdev->view_max.x = 640;
+               pdev->view_max.y = 480;
+               pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA;
+                pdev->abs_max.x = 640;
+                pdev->abs_max.y = 480;
+               pdev->vcinterface = 3;
+               pdev->vendpoint = 4;
+               pdev->frame_header_size = 0;
+               pdev->frame_trailer_size = 0;
+               break;
+       case 720:
+       case 730:
+       case 740:
+       case 750:
+               pdev->view_min.x = 160;
+               pdev->view_min.y = 120;
+               pdev->view_max.x = 640;
+               pdev->view_max.y = 480;
+               pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA;
+                pdev->abs_max.x = 640;
+                pdev->abs_max.y = 480;
+               pdev->vcinterface = 3;
+               pdev->vendpoint = 5;
+               pdev->frame_header_size = TOUCAM_HEADER_SIZE;
+               pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE;
+               break;
+       }
+       Debug("type = %d\n",pdev->type);
+       pdev->vpalette = VIDEO_PALETTE_YUV420P; /* default */
+       pdev->view_min.size = pdev->view_min.x * pdev->view_min.y;
+       pdev->view_max.size = pdev->view_max.x * pdev->view_max.y;
+       /* length of image, in YUV format; always allocate enough memory. */
+       pdev->len_per_image = (pdev->abs_max.x * pdev->abs_max.y * 3) / 2;
+}
+
+
diff --git a/drivers/usb/media/pwc/pwc-nala.h b/drivers/usb/media/pwc/pwc-nala.h
new file mode 100644 (file)
index 0000000..e6c5cb6
--- /dev/null
@@ -0,0 +1,66 @@
+   /* SQCIF */
+   {
+      {0, 0, {0x04, 0x01, 0x03}},
+      {8, 0, {0x05, 0x01, 0x03}},
+      {7, 0, {0x08, 0x01, 0x03}},
+      {7, 0, {0x0A, 0x01, 0x03}},
+      {6, 0, {0x0C, 0x01, 0x03}},
+      {5, 0, {0x0F, 0x01, 0x03}},
+      {4, 0, {0x14, 0x01, 0x03}},
+      {3, 0, {0x18, 0x01, 0x03}},
+   },
+   /* QSIF */
+   {
+      {0},
+      {0},
+      {0},
+      {0},
+      {0},
+      {0},
+      {0},
+      {0},
+   },
+   /* QCIF */
+   {
+      {0, 0, {0x04, 0x01, 0x02}},
+      {8, 0, {0x05, 0x01, 0x02}},
+      {7, 0, {0x08, 0x01, 0x02}},
+      {6, 0, {0x0A, 0x01, 0x02}},
+      {5, 0, {0x0C, 0x01, 0x02}},
+      {4, 0, {0x0F, 0x01, 0x02}},
+      {1, 0, {0x14, 0x01, 0x02}},
+      {1, 0, {0x18, 0x01, 0x02}},
+   },
+   /* SIF */
+   {
+      {0},
+      {0},
+      {0},
+      {0},
+      {0},
+      {0},
+      {0},
+      {0},
+   },
+   /* CIF */
+   {
+      {4, 0, {0x04, 0x01, 0x01}},
+      {7, 1, {0x05, 0x03, 0x01}},
+      {6, 1, {0x08, 0x03, 0x01}},
+      {4, 1, {0x0A, 0x03, 0x01}},
+      {3, 1, {0x0C, 0x03, 0x01}},
+      {2, 1, {0x0F, 0x03, 0x01}},
+      {0},
+      {0},
+   },
+   /* VGA */
+   {  
+      {0},
+      {0},
+      {0},
+      {0},
+      {0},
+      {0},
+      {0},
+      {0},
+   },
diff --git a/drivers/usb/media/pwc/pwc-timon.h b/drivers/usb/media/pwc/pwc-timon.h
new file mode 100644 (file)
index 0000000..a86b378
--- /dev/null
@@ -0,0 +1,61 @@
+/* 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.
+*/
+
+#ifndef PWC_TIMON_H
+#define PWC_TIMON_H
+
+#include "pwc-ioctl.h"
+
+struct Timon_table_entry
+{
+       char alternate;                 /* USB alternate interface */
+       unsigned short packetsize;      /* Normal packet size */
+       unsigned short bandlength;      /* Bandlength when decompressing */
+       unsigned char mode[13];         /* precomputed mode settings for cam */
+};
+
+const extern struct Timon_table_entry Timon_table[PSZ_MAX][6][4];
+const extern unsigned int TimonRomTable [16][2][16][8];
+
+
+#endif
+
+
diff --git a/drivers/usb/media/pwc/pwc-uncompress.h b/drivers/usb/media/pwc/pwc-uncompress.h
new file mode 100644 (file)
index 0000000..d3b9250
--- /dev/null
@@ -0,0 +1,41 @@
+/* (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
+*/
+
+/* This file is the bridge between the kernel module and the plugin; it
+   describes the structures and datatypes used in both modules. Any
+   significant change should be reflected by increasing the 
+   pwc_decompressor_version major number.
+ */
+#ifndef PWC_UNCOMPRESS_H
+#define PWC_UNCOMPRESS_H
+
+#include <linux/config.h>
+
+#include "pwc-ioctl.h"
+
+/* from pwc-dec.h */
+#define PWCX_FLAG_PLANAR        0x0001
+/* */
+
+#endif
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
new file mode 100644 (file)
index 0000000..2fd1226
--- /dev/null
@@ -0,0 +1,3147 @@
+/*
+ * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles
+ *
+ * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria
+ *
+ * If distributed as part of the Linux kernel, this code is licensed under the
+ * terms of the GPL v2.
+ *
+ * Otherwise, the following license terms apply:
+ *
+ * * 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) The name of the author may not be used to endorse or promote products
+ * *    derived from this software without specific psisusbr written permission.
+ * *
+ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED 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.
+ *
+ * Author:     Thomas Winischhofer <thomas@winischhofer.net>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/kref.h>
+#include <linux/usb.h>
+#include <linux/smp_lock.h>
+
+#include "sisusb.h"
+
+#define SISUSB_DONTSYNC
+
+/* Forward declarations / clean-up routines */
+
+static struct usb_driver sisusb_driver;
+
+static DECLARE_MUTEX(disconnect_sem);
+
+static void
+sisusb_free_buffers(struct sisusb_usb_data *sisusb)
+{
+       int i;
+
+       for (i = 0; i < NUMOBUFS; i++) {
+               if (sisusb->obuf[i]) {
+                       usb_buffer_free(sisusb->sisusb_dev, sisusb->obufsize,
+                               sisusb->obuf[i], sisusb->transfer_dma_out[i]);
+                       sisusb->obuf[i] = NULL;
+               }
+       }
+       if (sisusb->ibuf) {
+               usb_buffer_free(sisusb->sisusb_dev, sisusb->ibufsize,
+                       sisusb->ibuf, sisusb->transfer_dma_in);
+               sisusb->ibuf = NULL;
+       }
+}
+
+static void
+sisusb_free_urbs(struct sisusb_usb_data *sisusb)
+{
+       int i;
+
+       for (i = 0; i < NUMOBUFS; i++) {
+               usb_free_urb(sisusb->sisurbout[i]);
+               sisusb->sisurbout[i] = NULL;
+       }
+       usb_free_urb(sisusb->sisurbin);
+       sisusb->sisurbin = NULL;
+}
+
+/* Level 0: USB transport layer */
+
+/* 1. out-bulks */
+
+/* out-urb management */
+
+/* Return 1 if all free, 0 otherwise */
+static int
+sisusb_all_free(struct sisusb_usb_data *sisusb)
+{
+       int i;
+
+       for (i = 0; i < sisusb->numobufs; i++) {
+
+               if (sisusb->urbstatus[i] & SU_URB_BUSY)
+                       return 0;
+
+       }
+
+       return 1;
+}
+
+/* Kill all busy URBs */
+static void
+sisusb_kill_all_busy(struct sisusb_usb_data *sisusb)
+{
+       int i;
+
+       if (sisusb_all_free(sisusb))
+               return;
+
+       for (i = 0; i < sisusb->numobufs; i++) {
+
+               if (sisusb->urbstatus[i] & SU_URB_BUSY)
+                       usb_kill_urb(sisusb->sisurbout[i]);
+
+       }
+}
+
+/* Return 1 if ok, 0 if error (not all complete within timeout) */
+static int
+sisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb)
+{
+       int timeout = 5 * HZ, i = 1;
+
+       wait_event_timeout(sisusb->wait_q,
+                               (i = sisusb_all_free(sisusb)),
+                                timeout);
+
+       return i;
+}
+
+static int
+sisusb_outurb_available(struct sisusb_usb_data *sisusb)
+{
+       int i;
+
+       for (i = 0; i < sisusb->numobufs; i++) {
+
+               if ((sisusb->urbstatus[i] & (SU_URB_BUSY|SU_URB_ALLOC)) == 0)
+                       return i;
+
+       }
+
+       return -1;
+}
+
+static int
+sisusb_get_free_outbuf(struct sisusb_usb_data *sisusb)
+{
+       int i, timeout = 5 * HZ;
+
+       wait_event_timeout(sisusb->wait_q,
+                               ((i = sisusb_outurb_available(sisusb)) >= 0),
+                               timeout);
+
+       return i;
+}
+
+static int
+sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb)
+{
+       int i;
+
+       i = sisusb_outurb_available(sisusb);
+
+       if (i >= 0)
+               sisusb->urbstatus[i] |= SU_URB_ALLOC;
+
+       return i;
+}
+
+static void
+sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index)
+{
+       if ((index >= 0) && (index < sisusb->numobufs))
+               sisusb->urbstatus[index] &= ~SU_URB_ALLOC;
+}
+
+/* completion callback */
+
+static void
+sisusb_bulk_completeout(struct urb *urb, struct pt_regs *regs)
+{
+       struct sisusb_urb_context *context = urb->context;
+       struct sisusb_usb_data *sisusb;
+
+       if (!context)
+               return;
+
+       sisusb = context->sisusb;
+
+       if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
+               return;
+
+#ifndef SISUSB_DONTSYNC
+       if (context->actual_length)
+               *(context->actual_length) += urb->actual_length;
+#endif
+
+       sisusb->urbstatus[context->urbindex] &= ~SU_URB_BUSY;
+       wake_up(&sisusb->wait_q);
+}
+
+static int
+sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, void *data,
+               int len, int *actual_length, int timeout, unsigned int tflags,
+               dma_addr_t transfer_dma)
+{
+       struct urb *urb = sisusb->sisurbout[index];
+       int retval, byteswritten = 0;
+
+       /* Set up URB */
+       urb->transfer_flags = 0;
+
+       usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
+               sisusb_bulk_completeout, &sisusb->urbout_context[index]);
+
+       urb->transfer_flags |= (tflags | URB_ASYNC_UNLINK);
+       urb->actual_length = 0;
+
+       if ((urb->transfer_dma = transfer_dma))
+               urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+       /* Set up context */
+       sisusb->urbout_context[index].actual_length = (timeout) ?
+                                               NULL : actual_length;
+
+       /* Declare this urb/buffer in use */
+       sisusb->urbstatus[index] |= SU_URB_BUSY;
+
+       /* Submit URB */
+       retval = usb_submit_urb(urb, GFP_ATOMIC);
+
+       /* If OK, and if timeout > 0, wait for completion */
+       if ((retval == 0) && timeout) {
+               wait_event_timeout(sisusb->wait_q,
+                                  (!(sisusb->urbstatus[index] & SU_URB_BUSY)),
+                                  timeout);
+               if (sisusb->urbstatus[index] & SU_URB_BUSY) {
+                       /* URB timed out... kill it and report error */
+                       usb_kill_urb(urb);
+                       retval = -ETIMEDOUT;
+               } else {
+                       /* Otherwise, report urb status */
+                       retval = urb->status;
+                       byteswritten = urb->actual_length;
+               }
+       }
+
+       if (actual_length)
+               *actual_length = byteswritten;
+
+       return retval;
+}
+
+/* 2. in-bulks */
+
+/* completion callback */
+
+static void
+sisusb_bulk_completein(struct urb *urb, struct pt_regs *regs)
+{
+       struct sisusb_usb_data *sisusb = urb->context;
+
+       if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
+               return;
+
+       sisusb->completein = 1;
+       wake_up(&sisusb->wait_q);
+}
+
+static int
+sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data, int len,
+               int *actual_length, int timeout, unsigned int tflags, dma_addr_t transfer_dma)
+{
+       struct urb *urb = sisusb->sisurbin;
+       int retval, readbytes = 0;
+
+       urb->transfer_flags = 0;
+
+       usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len,
+                       sisusb_bulk_completein, sisusb);
+
+       urb->transfer_flags |= (tflags | URB_ASYNC_UNLINK);
+       urb->actual_length = 0;
+
+       if ((urb->transfer_dma = transfer_dma))
+               urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+       sisusb->completein = 0;
+       retval = usb_submit_urb(urb, GFP_ATOMIC);
+       if (retval == 0) {
+               wait_event_timeout(sisusb->wait_q, sisusb->completein, timeout);
+               if (!sisusb->completein) {
+                       /* URB timed out... kill it and report error */
+                       usb_kill_urb(urb);
+                       retval = -ETIMEDOUT;
+               } else {
+                       /* URB completed within timout */
+                       retval = urb->status;
+                       readbytes = urb->actual_length;
+               }
+       }
+
+       if (actual_length)
+               *actual_length = readbytes;
+
+       return retval;
+}
+
+
+/* Level 1:  */
+
+/* Send a bulk message of variable size
+ *
+ * To copy the data from userspace, give pointer to "userbuffer",
+ * to copy from (non-DMA) kernel memory, give "kernbuffer". If
+ * both of these are NULL, it is assumed, that the transfer
+ * buffer "sisusb->obuf[index]" is set up with the data to send.
+ * Index is ignored if either kernbuffer or userbuffer is set.
+ * If async is nonzero, URBs will be sent without waiting for
+ * completion of the previous URB.
+ *
+ * (return 0 on success)
+ */
+
+static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
+               char *kernbuffer, const char __user *userbuffer, int index,
+               ssize_t *bytes_written, unsigned int tflags, int async)
+{
+       int result = 0, retry, count = len;
+       int passsize, thispass, transferred_len = 0;
+       int fromuser = (userbuffer != NULL) ? 1 : 0;
+       int fromkern = (kernbuffer != NULL) ? 1 : 0;
+       unsigned int pipe;
+       char *buffer;
+
+       (*bytes_written) = 0;
+
+       /* Sanity check */
+       if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
+               return -ENODEV;
+
+       /* If we copy data from kernel or userspace, force the
+        * allocation of a buffer/urb. If we have the data in
+        * the transfer buffer[index] already, reuse the buffer/URB
+        * if the length is > buffer size. (So, transmitting
+        * large data amounts directly from the transfer buffer
+        * treats the buffer as a ring buffer. However, we need
+        * to sync in this case.)
+        */
+       if (fromuser || fromkern)
+               index = -1;
+       else if (len > sisusb->obufsize)
+               async = 0;
+
+       pipe = usb_sndbulkpipe(sisusb->sisusb_dev, ep);
+
+       do {
+               passsize = thispass = (sisusb->obufsize < count) ?
+                                               sisusb->obufsize : count;
+
+               if (index < 0)
+                       index = sisusb_get_free_outbuf(sisusb);
+
+               if (index < 0)
+                       return -EIO;
+
+               buffer = sisusb->obuf[index];
+
+               if (fromuser) {
+
+                       if (copy_from_user(buffer, userbuffer, passsize))
+                               return -EFAULT;
+
+                       userbuffer += passsize;
+
+               } else if (fromkern) {
+
+                       memcpy(buffer, kernbuffer, passsize);
+                       kernbuffer += passsize;
+
+               }
+
+               retry = 5;
+               while (thispass) {
+
+                       if (!sisusb->sisusb_dev)
+                               return -ENODEV;
+
+                       result = sisusb_bulkout_msg(sisusb,
+                                               index,
+                                               pipe,
+                                               buffer,
+                                               thispass,
+                                               &transferred_len,
+                                               async ? 0 : 5 * HZ,
+                                               tflags,
+                                               sisusb->transfer_dma_out[index]);
+
+                       if (result == -ETIMEDOUT) {
+
+                               /* Will not happen if async */
+                               if (!retry--)
+                                       return -ETIME;
+
+                               continue;
+
+                       } else if ((result == 0) && !async && transferred_len) {
+
+                               thispass -= transferred_len;
+                               if (thispass) {
+                                       if (sisusb->transfer_dma_out) {
+                                               /* If DMA, copy remaining
+                                                * to beginning of buffer
+                                                */
+                                               memcpy(buffer,
+                                                      buffer + transferred_len,
+                                                      thispass);
+                                       } else {
+                                               /* If not DMA, simply increase
+                                                * the pointer
+                                                */
+                                               buffer += transferred_len;
+                                       }
+                               }
+
+                       } else
+                               break;
+               };
+
+               if (result)
+                       return result;
+
+               (*bytes_written) += passsize;
+               count            -= passsize;
+
+               /* Force new allocation in next iteration */
+               if (fromuser || fromkern)
+                       index = -1;
+
+       } while (count > 0);
+
+       if (async) {
+#ifdef SISUSB_DONTSYNC
+               (*bytes_written) = len;
+               /* Some URBs/buffers might be busy */
+#else
+               sisusb_wait_all_out_complete(sisusb);
+               (*bytes_written) = transferred_len;
+               /* All URBs and all buffers are available */
+#endif
+       }
+
+       return ((*bytes_written) == len) ? 0 : -EIO;
+}
+
+/* Receive a bulk message of variable size
+ *
+ * To copy the data to userspace, give pointer to "userbuffer",
+ * to copy to kernel memory, give "kernbuffer". One of them
+ * MUST be set. (There is no technique for letting the caller
+ * read directly from the ibuf.)
+ *
+ */
+
+static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
+               void *kernbuffer, char __user *userbuffer, ssize_t *bytes_read,
+               unsigned int tflags)
+{
+       int result = 0, retry, count = len;
+       int bufsize, thispass, transferred_len;
+       unsigned int pipe;
+       char *buffer;
+
+       (*bytes_read) = 0;
+
+       /* Sanity check */
+       if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
+               return -ENODEV;
+
+       pipe = usb_rcvbulkpipe(sisusb->sisusb_dev, ep);
+       buffer = sisusb->ibuf;
+       bufsize = sisusb->ibufsize;
+
+       retry = 5;
+
+#ifdef SISUSB_DONTSYNC
+       if (!(sisusb_wait_all_out_complete(sisusb)))
+               return -EIO;
+#endif
+
+       while (count > 0) {
+
+               if (!sisusb->sisusb_dev)
+                       return -ENODEV;
+
+               thispass = (bufsize < count) ? bufsize : count;
+
+               result = sisusb_bulkin_msg(sisusb,
+                                          pipe,
+                                          buffer,
+                                          thispass,
+                                          &transferred_len,
+                                          5 * HZ,
+                                          tflags,
+                                          sisusb->transfer_dma_in);
+
+               if (transferred_len)
+                       thispass = transferred_len;
+
+               else if (result == -ETIMEDOUT) {
+
+                       if (!retry--)
+                               return -ETIME;
+
+                       continue;
+
+               } else
+                       return -EIO;
+
+
+               if (thispass) {
+
+                       (*bytes_read) += thispass;
+                       count         -= thispass;
+
+                       if (userbuffer) {
+
+                               if (copy_to_user(userbuffer, buffer, thispass))
+                                       return -EFAULT;
+
+                               userbuffer += thispass;
+
+                       } else {
+
+                               memcpy(kernbuffer, buffer, thispass);
+                               kernbuffer += thispass;
+
+                       }
+
+               }
+
+       }
+
+       return ((*bytes_read) == len) ? 0 : -EIO;
+}
+
+static int sisusb_send_packet(struct sisusb_usb_data *sisusb, int len,
+                                               struct sisusb_packet *packet)
+{
+       int ret;
+       ssize_t bytes_transferred = 0;
+       __le32 tmp;
+
+       if (len == 6)
+               packet->data = 0;
+
+#ifdef SISUSB_DONTSYNC
+       if (!(sisusb_wait_all_out_complete(sisusb)))
+               return 1;
+#endif
+
+       /* Eventually correct endianness */
+       SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
+
+       /* 1. send the packet */
+       ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_OUT, len,
+                       (char *)packet, NULL, 0, &bytes_transferred, 0, 0);
+
+       if ((ret == 0) && (len == 6)) {
+
+               /* 2. if packet len == 6, it means we read, so wait for 32bit
+                *    return value and write it to packet->data
+                */
+               ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_GFX_IN, 4,
+                               (char *)&tmp, NULL, &bytes_transferred, 0);
+
+               packet->data = le32_to_cpu(tmp);
+       }
+
+       return ret;
+}
+
+static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len,
+                                       struct sisusb_packet *packet,
+                                       unsigned int tflags)
+{
+       int ret;
+       ssize_t bytes_transferred = 0;
+       __le32 tmp;
+
+       if (len == 6)
+               packet->data = 0;
+
+#ifdef SISUSB_DONTSYNC
+       if (!(sisusb_wait_all_out_complete(sisusb)))
+               return 1;
+#endif
+
+       /* Eventually correct endianness */
+       SISUSB_CORRECT_ENDIANNESS_PACKET(packet);
+
+       /* 1. send the packet */
+       ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_BRIDGE_OUT, len,
+                       (char *)packet, NULL, 0, &bytes_transferred, tflags, 0);
+
+       if ((ret == 0) && (len == 6)) {
+
+               /* 2. if packet len == 6, it means we read, so wait for 32bit
+                *    return value and write it to packet->data
+                */
+               ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_BRIDGE_IN, 4,
+                               (char *)&tmp, NULL, &bytes_transferred, 0);
+
+               packet->data = le32_to_cpu(tmp);
+       }
+
+       return ret;
+}
+
+/* access video memory and mmio (return 0 on success) */
+
+/* Low level */
+
+/* The following routines assume being used to transfer byte, word,
+ * long etc.
+ * This means that they assume "data" in machine endianness format.
+ */
+
+static int sisusb_write_memio_byte(struct sisusb_usb_data *sisusb, int type,
+                                                       u32 addr, u8 data)
+{
+       struct sisusb_packet packet;
+       int ret;
+
+       packet.header  = (1 << (addr & 3)) | (type << 6);
+       packet.address = addr & ~3;
+       packet.data    = data << ((addr & 3) << 3);
+       ret = sisusb_send_packet(sisusb, 10, &packet);
+       return ret;
+}
+
+static int sisusb_write_memio_word(struct sisusb_usb_data *sisusb, int type,
+                                                       u32 addr, u16 data)
+{
+       struct sisusb_packet packet;
+       int ret = 0;
+
+       packet.address = addr & ~3;
+
+       switch (addr & 3) {
+               case 0:
+                       packet.header = (type << 6) | 0x0003;
+                       packet.data   = (u32)data;
+                       ret = sisusb_send_packet(sisusb, 10, &packet);
+                       break;
+               case 1:
+                       packet.header = (type << 6) | 0x0006;
+                       packet.data   = (u32)data << 8;
+                       ret = sisusb_send_packet(sisusb, 10, &packet);
+                       break;
+               case 2:
+                       packet.header = (type << 6) | 0x000c;
+                       packet.data   = (u32)data << 16;
+                       ret = sisusb_send_packet(sisusb, 10, &packet);
+                       break;
+               case 3:
+                       packet.header = (type << 6) | 0x0008;
+                       packet.data   = (u32)data << 24;
+                       ret = sisusb_send_packet(sisusb, 10, &packet);
+                       packet.header = (type << 6) | 0x0001;
+                       packet.address = (addr & ~3) + 4;
+                       packet.data   = (u32)data >> 8;
+                       ret |= sisusb_send_packet(sisusb, 10, &packet);
+       }
+
+       return ret;
+}
+
+static int sisusb_write_memio_24bit(struct sisusb_usb_data *sisusb, int type,
+                                                       u32 addr, u32 data)
+{
+       struct sisusb_packet packet;
+       int ret = 0;
+
+       packet.address = addr & ~3;
+
+       switch (addr & 3) {
+               case 0:
+                       packet.header  = (type << 6) | 0x0007;
+                       packet.data    = data & 0x00ffffff;
+                       ret = sisusb_send_packet(sisusb, 10, &packet);
+                       break;
+               case 1:
+                       packet.header  = (type << 6) | 0x000e;
+                       packet.data    = data << 8;
+                       ret = sisusb_send_packet(sisusb, 10, &packet);
+                       break;
+               case 2:
+                       packet.header  = (type << 6) | 0x000c;
+                       packet.data    = data << 16;
+                       ret = sisusb_send_packet(sisusb, 10, &packet);
+                       packet.header  = (type << 6) | 0x0001;
+                       packet.address = (addr & ~3) + 4;
+                       packet.data    = (data >> 16) & 0x00ff;
+                       ret |= sisusb_send_packet(sisusb, 10, &packet);
+                       break;
+               case 3:
+                       packet.header  = (type << 6) | 0x0008;
+                       packet.data    = data << 24;
+                       ret = sisusb_send_packet(sisusb, 10, &packet);
+                       packet.header  = (type << 6) | 0x0003;
+                       packet.address = (addr & ~3) + 4;
+                       packet.data    = (data >> 8) & 0xffff;
+                       ret |= sisusb_send_packet(sisusb, 10, &packet);
+       }
+
+       return ret;
+}
+
+static int sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type,
+                                                       u32 addr, u32 data)
+{
+       struct sisusb_packet packet;
+       int ret = 0;
+
+       packet.address = addr & ~3;
+
+       switch (addr & 3) {
+               case 0:
+                       packet.header  = (type << 6) | 0x000f;
+                       packet.data    = data;
+                       ret = sisusb_send_packet(sisusb, 10, &packet);
+                       break;
+               case 1:
+                       packet.header  = (type << 6) | 0x000e;
+                       packet.data    = data << 8;
+                       ret = sisusb_send_packet(sisusb, 10, &packet);
+                       packet.header  = (type << 6) | 0x0001;
+                       packet.address = (addr & ~3) + 4;
+                       packet.data    = data >> 24;
+                       ret |= sisusb_send_packet(sisusb, 10, &packet);
+                       break;
+               case 2:
+                       packet.header  = (type << 6) | 0x000c;
+                       packet.data    = data << 16;
+                       ret = sisusb_send_packet(sisusb, 10, &packet);
+                       packet.header  = (type << 6) | 0x0003;
+                       packet.address = (addr & ~3) + 4;
+                       packet.data    = data >> 16;
+                       ret |= sisusb_send_packet(sisusb, 10, &packet);
+                       break;
+               case 3:
+                       packet.header  = (type << 6) | 0x0008;
+                       packet.data    = data << 24;
+                       ret = sisusb_send_packet(sisusb, 10, &packet);
+                       packet.header  = (type << 6) | 0x0007;
+                       packet.address = (addr & ~3) + 4;
+                       packet.data    = data >> 8;
+                       ret |= sisusb_send_packet(sisusb, 10, &packet);
+       }
+
+       return ret;
+}
+
+/* The xxx_bulk routines copy a buffer of variable size. They treat the
+ * buffer as chars, therefore lsb/msb has to be corrected if using the
+ * byte/word/long/etc routines for speed-up
+ *
+ * If data is from userland, set "userbuffer" (and clear "kernbuffer"),
+ * if data is in kernel space, set "kernbuffer" (and clear "userbuffer");
+ * if neither "kernbuffer" nor "userbuffer" are given, it is assumed
+ * that the data already is in the transfer buffer "sisusb->obuf[index]".
+ */
+
+static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
+                               char *kernbuffer, int length,
+                               const char __user *userbuffer, int index,
+                               ssize_t *bytes_written)
+{
+       struct sisusb_packet packet;
+       int  ret = 0;
+       static int msgcount = 0;
+       u8   swap8, fromkern = kernbuffer ? 1 : 0;
+       u16  swap16;
+       u32  swap32, flag = (length >> 28) & 1;
+       char buf[4];
+
+       /* if neither kernbuffer not userbuffer are given, assume
+        * data in obuf
+        */
+       if (!fromkern && !userbuffer)
+               kernbuffer = sisusb->obuf[index];
+
+       (*bytes_written = 0);
+
+       length &= 0x00ffffff;
+
+       while (length) {
+
+           switch (length) {
+
+               case 0:
+                       return ret;
+
+               case 1:
+                       if (userbuffer) {
+                               if (get_user(swap8, (u8 __user *)userbuffer))
+                                       return -EFAULT;
+                       } else
+                               swap8 = kernbuffer[0];
+
+                       ret = sisusb_write_memio_byte(sisusb,
+                                                       SISUSB_TYPE_MEM,
+                                                       addr, swap8);
+
+                       if (!ret)
+                               (*bytes_written)++;
+
+                       return ret;
+
+               case 2:
+                       if (userbuffer) {
+                               if (get_user(swap16, (u16 __user *)userbuffer))
+                                       return -EFAULT;
+                       } else
+                               swap16 = (kernbuffer[0] << 8) | kernbuffer[1];
+
+                       ret = sisusb_write_memio_word(sisusb,
+                                                       SISUSB_TYPE_MEM,
+                                                       addr,
+                                                       swap16);
+
+                       if (!ret)
+                               (*bytes_written) += 2;
+
+                       return ret;
+
+               case 3:
+                       if (userbuffer) {
+                               if (copy_from_user(&buf, userbuffer, 3))
+                                       return -EFAULT;
+
+                               swap32 = (buf[0] << 16) |
+                                        (buf[1] <<  8) |
+                                        buf[2];
+                       } else
+                               swap32 = (kernbuffer[0] << 16) |
+                                        (kernbuffer[1] <<  8) |
+                                        kernbuffer[2];
+
+                       ret = sisusb_write_memio_24bit(sisusb,
+                                                       SISUSB_TYPE_MEM,
+                                                       addr,
+                                                       swap32);
+
+                       if (!ret)
+                               (*bytes_written) += 3;
+
+                       return ret;
+
+               case 4:
+                       if (userbuffer) {
+                               if (get_user(swap32, (u32 __user *)userbuffer))
+                                       return -EFAULT;
+                       } else
+                               swap32 = (kernbuffer[0] << 24) |
+                                        (kernbuffer[1] << 16) |
+                                        (kernbuffer[2] <<  8) |
+                                        kernbuffer[3];
+
+                       ret = sisusb_write_memio_long(sisusb,
+                                                       SISUSB_TYPE_MEM,
+                                                       addr,
+                                                       swap32);
+                       if (!ret)
+                               (*bytes_written) += 4;
+
+                       return ret;
+
+               default:
+                       if ((length & ~3) > 0x10000) {
+
+                          packet.header  = 0x001f;
+                          packet.address = 0x000001d4;
+                          packet.data    = addr;
+                          ret = sisusb_send_bridge_packet(sisusb, 10,
+                                                               &packet, 0);
+                          packet.header  = 0x001f;
+                          packet.address = 0x000001d0;
+                          packet.data    = (length & ~3);
+                          ret |= sisusb_send_bridge_packet(sisusb, 10,
+                                                               &packet, 0);
+                          packet.header  = 0x001f;
+                          packet.address = 0x000001c0;
+                          packet.data    = flag | 0x16;
+                          ret |= sisusb_send_bridge_packet(sisusb, 10,
+                                                               &packet, 0);
+                          if (userbuffer) {
+                               ret |= sisusb_send_bulk_msg(sisusb,
+                                                       SISUSB_EP_GFX_LBULK_OUT,
+                                                       (length & ~3),
+                                                       NULL, userbuffer, 0,
+                                                       bytes_written, 0, 1);
+                               userbuffer += (*bytes_written);
+                          } else if (fromkern) {
+                               ret |= sisusb_send_bulk_msg(sisusb,
+                                                       SISUSB_EP_GFX_LBULK_OUT,
+                                                       (length & ~3),
+                                                       kernbuffer, NULL, 0,
+                                                       bytes_written, 0, 1);
+                               kernbuffer += (*bytes_written);
+                          } else {
+                       ret |= sisusb_send_bulk_msg(sisusb,
+                                                       SISUSB_EP_GFX_LBULK_OUT,
+                                                       (length & ~3),
+                                                       NULL, NULL, index,
+                                                       bytes_written, 0, 1);
+                               kernbuffer += ((*bytes_written) &
+                                               (sisusb->obufsize-1));
+                          }
+
+                       } else {
+
+                          packet.header  = 0x001f;
+                          packet.address = 0x00000194;
+                          packet.data    = addr;
+                          ret = sisusb_send_bridge_packet(sisusb, 10,
+                                                               &packet, 0);
+                          packet.header  = 0x001f;
+                          packet.address = 0x00000190;
+                          packet.data    = (length & ~3);
+                          ret |= sisusb_send_bridge_packet(sisusb, 10,
+                                                               &packet, 0);
+                          if (sisusb->flagb0 != 0x16) {
+                               packet.header  = 0x001f;
+                               packet.address = 0x00000180;
+                               packet.data    = flag | 0x16;
+                               ret |= sisusb_send_bridge_packet(sisusb, 10,
+                                                               &packet, 0);
+                               sisusb->flagb0 = 0x16;
+                          }
+                          if (userbuffer) {
+                               ret |= sisusb_send_bulk_msg(sisusb,
+                                                       SISUSB_EP_GFX_BULK_OUT,
+                                                       (length & ~3),
+                                                       NULL, userbuffer, 0,
+                                                       bytes_written, 0, 1);
+                               userbuffer += (*bytes_written);
+                          } else if (fromkern) {
+                               ret |= sisusb_send_bulk_msg(sisusb,
+                                                       SISUSB_EP_GFX_BULK_OUT,
+                                                       (length & ~3),
+                                                       kernbuffer, NULL, 0,
+                                                       bytes_written, 0, 1);
+                               kernbuffer += (*bytes_written);
+                          } else {
+                               ret |= sisusb_send_bulk_msg(sisusb,
+                                                       SISUSB_EP_GFX_BULK_OUT,
+                                                       (length & ~3),
+                                                       NULL, NULL, index,
+                                                       bytes_written, 0, 1);
+                               kernbuffer += ((*bytes_written) &
+                                               (sisusb->obufsize-1));
+                          }
+                       }
+                       if (ret) {
+                               msgcount++;
+                               if (msgcount < 500)
+                                       printk(KERN_ERR
+                                               "sisusbvga[%d]: Wrote %zd of "
+                                               "%d bytes, error %d\n",
+                                               sisusb->minor, *bytes_written,
+                                               length, ret);
+                               else if (msgcount == 500)
+                                       printk(KERN_ERR
+                                               "sisusbvga[%d]: Too many errors"
+                                               ", logging stopped\n",
+                                               sisusb->minor);
+                       }
+                       addr += (*bytes_written);
+                       length -= (*bytes_written);
+           }
+
+           if (ret)
+               break;
+
+       }
+
+       return ret ? -EIO : 0;
+}
+
+static int sisusb_read_memio_byte(struct sisusb_usb_data *sisusb, int type,
+                                                       u32 addr, u8 *data)
+{
+       struct sisusb_packet packet;
+       int ret;
+
+       CLEARPACKET(&packet);
+       packet.header  = (1 << (addr & 3)) | (type << 6);
+       packet.address = addr & ~3;
+       ret = sisusb_send_packet(sisusb, 6, &packet);
+       *data = (u8)(packet.data >> ((addr & 3) << 3));
+       return ret;
+}
+
+static int sisusb_read_memio_word(struct sisusb_usb_data *sisusb, int type,
+                                                       u32 addr, u16 *data)
+{
+       struct sisusb_packet packet;
+       int ret = 0;
+
+       CLEARPACKET(&packet);
+
+       packet.address = addr & ~3;
+
+       switch (addr & 3) {
+               case 0:
+                       packet.header = (type << 6) | 0x0003;
+                       ret = sisusb_send_packet(sisusb, 6, &packet);
+                       *data = (u16)(packet.data);
+                       break;
+               case 1:
+                       packet.header = (type << 6) | 0x0006;
+                       ret = sisusb_send_packet(sisusb, 6, &packet);
+                       *data = (u16)(packet.data >> 8);
+                       break;
+               case 2:
+                       packet.header = (type << 6) | 0x000c;
+                       ret = sisusb_send_packet(sisusb, 6, &packet);
+                       *data = (u16)(packet.data >> 16);
+                       break;
+               case 3:
+                       packet.header = (type << 6) | 0x0008;
+                       ret = sisusb_send_packet(sisusb, 6, &packet);
+                       *data = (u16)(packet.data >> 24);
+                       packet.header = (type << 6) | 0x0001;
+                       packet.address = (addr & ~3) + 4;
+                       ret |= sisusb_send_packet(sisusb, 6, &packet);
+                       *data |= (u16)(packet.data << 8);
+       }
+
+       return ret;
+}
+
+static int sisusb_read_memio_24bit(struct sisusb_usb_data *sisusb, int type,
+                                                       u32 addr, u32 *data)
+{
+       struct sisusb_packet packet;
+       int ret = 0;
+
+       packet.address = addr & ~3;
+
+       switch (addr & 3) {
+               case 0:
+                       packet.header  = (type << 6) | 0x0007;
+                       ret = sisusb_send_packet(sisusb, 6, &packet);
+                       *data = packet.data & 0x00ffffff;
+                       break;
+               case 1:
+                       packet.header  = (type << 6) | 0x000e;
+                       ret = sisusb_send_packet(sisusb, 6, &packet);
+                       *data = packet.data >> 8;
+                       break;
+               case 2:
+                       packet.header  = (type << 6) | 0x000c;
+                       ret = sisusb_send_packet(sisusb, 6, &packet);
+                       *data = packet.data >> 16;
+                       packet.header  = (type << 6) | 0x0001;
+                       packet.address = (addr & ~3) + 4;
+                       ret |= sisusb_send_packet(sisusb, 6, &packet);
+                       *data |= ((packet.data & 0xff) << 16);
+                       break;
+               case 3:
+                       packet.header  = (type << 6) | 0x0008;
+                       ret = sisusb_send_packet(sisusb, 6, &packet);
+                       *data = packet.data >> 24;
+                       packet.header  = (type << 6) | 0x0003;
+                       packet.address = (addr & ~3) + 4;
+                       ret |= sisusb_send_packet(sisusb, 6, &packet);
+                       *data |= ((packet.data & 0xffff) << 8);
+       }
+
+       return ret;
+}
+
+static int sisusb_read_memio_long(struct sisusb_usb_data *sisusb, int type,
+                                                       u32 addr, u32 *data)
+{
+       struct sisusb_packet packet;
+       int ret = 0;
+
+       packet.address = addr & ~3;
+
+       switch (addr & 3) {
+               case 0:
+                       packet.header  = (type << 6) | 0x000f;
+                       ret = sisusb_send_packet(sisusb, 6, &packet);
+                       *data = packet.data;
+                       break;
+               case 1:
+                       packet.header  = (type << 6) | 0x000e;
+                       ret = sisusb_send_packet(sisusb, 6, &packet);
+                       *data = packet.data >> 8;
+                       packet.header  = (type << 6) | 0x0001;
+                       packet.address = (addr & ~3) + 4;
+                       ret |= sisusb_send_packet(sisusb, 6, &packet);
+                       *data |= (packet.data << 24);
+                       break;
+               case 2:
+                       packet.header  = (type << 6) | 0x000c;
+                       ret = sisusb_send_packet(sisusb, 6, &packet);
+                       *data = packet.data >> 16;
+                       packet.header  = (type << 6) | 0x0003;
+                       packet.address = (addr & ~3) + 4;
+                       ret |= sisusb_send_packet(sisusb, 6, &packet);
+                       *data |= (packet.data << 16);
+                       break;
+               case 3:
+                       packet.header  = (type << 6) | 0x0008;
+                       ret = sisusb_send_packet(sisusb, 6, &packet);
+                       *data = packet.data >> 24;
+                       packet.header  = (type << 6) | 0x0007;
+                       packet.address = (addr & ~3) + 4;
+                       ret |= sisusb_send_packet(sisusb, 6, &packet);
+                       *data |= (packet.data << 8);
+       }
+
+       return ret;
+}
+
+static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
+                               char *kernbuffer, int length,
+                               char __user *userbuffer, ssize_t *bytes_read)
+{
+       int ret = 0;
+       char buf[4];
+       u16 swap16;
+       u32 swap32;
+
+       (*bytes_read = 0);
+
+       length &= 0x00ffffff;
+
+       while (length) {
+
+           switch (length) {
+
+               case 0:
+                       return ret;
+
+               case 1:
+
+                       ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM,
+                                                               addr, &buf[0]);
+                       if (!ret) {
+                               (*bytes_read)++;
+                               if (userbuffer) {
+                                       if (put_user(buf[0],
+                                               (u8 __user *)userbuffer)) {
+                                               return -EFAULT;
+                                       }
+                               } else {
+                                       kernbuffer[0] = buf[0];
+                               }
+                       }
+                       return ret;
+
+               case 2:
+                       ret |= sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM,
+                                                               addr, &swap16);
+                       if (!ret) {
+                               (*bytes_read) += 2;
+                               if (userbuffer) {
+                                       if (put_user(swap16,
+                                               (u16 __user *)userbuffer))
+                                               return -EFAULT;
+                               } else {
+                                       kernbuffer[0] = swap16 >> 8;
+                                       kernbuffer[1] = swap16 & 0xff;
+                               }
+                       }
+                       return ret;
+
+               case 3:
+                       ret |= sisusb_read_memio_24bit(sisusb, SISUSB_TYPE_MEM,
+                                                               addr, &swap32);
+                       if (!ret) {
+                               (*bytes_read) += 3;
+                               buf[0] = (swap32 >> 16) & 0xff;
+                               buf[1] = (swap32 >> 8) & 0xff;
+                               buf[2] = swap32 & 0xff;
+                               if (userbuffer) {
+                                       if (copy_to_user(userbuffer, &buf[0], 3))
+                                               return -EFAULT;
+                               } else {
+                                       kernbuffer[0] = buf[0];
+                                       kernbuffer[1] = buf[1];
+                                       kernbuffer[2] = buf[2];
+                               }
+                       }
+                       return ret;
+
+               default:
+                       ret |= sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM,
+                                                               addr, &swap32);
+                       if (!ret) {
+                               (*bytes_read) += 4;
+                               if (userbuffer) {
+                                       if (put_user(swap32,
+                                               (u32 __user *)userbuffer))
+                                               return -EFAULT;
+
+                                       userbuffer += 4;
+                               } else {
+                                       kernbuffer[0] = (swap32 >> 24) & 0xff;
+                                       kernbuffer[1] = (swap32 >> 16) & 0xff;
+                                       kernbuffer[2] = (swap32 >> 8) & 0xff;
+                                       kernbuffer[3] = swap32 & 0xff;
+                                       kernbuffer += 4;
+                               }
+                               addr += 4;
+                               length -= 4;
+                       }
+#if 0          /* That does not work, as EP 2 is an OUT EP! */
+               default:
+                       CLEARPACKET(&packet);
+                       packet.header  = 0x001f;
+                       packet.address = 0x000001a0;
+                       packet.data    = 0x00000006;
+                       ret |= sisusb_send_bridge_packet(sisusb, 10,
+                                                               &packet, 0);
+                       packet.header  = 0x001f;
+                       packet.address = 0x000001b0;
+                       packet.data    = (length & ~3) | 0x40000000;
+                       ret |= sisusb_send_bridge_packet(sisusb, 10,
+                                                               &packet, 0);
+                       packet.header  = 0x001f;
+                       packet.address = 0x000001b4;
+                       packet.data    = addr;
+                       ret |= sisusb_send_bridge_packet(sisusb, 10,
+                                                               &packet, 0);
+                       packet.header  = 0x001f;
+                       packet.address = 0x000001a4;
+                       packet.data    = 0x00000001;
+                       ret |= sisusb_send_bridge_packet(sisusb, 10,
+                                                               &packet, 0);
+                       if (userbuffer) {
+                               ret |= sisusb_recv_bulk_msg(sisusb,
+                                                       SISUSB_EP_GFX_BULK_IN,
+                                                       (length & ~3),
+                                                       NULL, userbuffer,
+                                                       bytes_read, 0);
+                               if (!ret) userbuffer += (*bytes_read);
+                       } else {
+                               ret |= sisusb_recv_bulk_msg(sisusb,
+                                                       SISUSB_EP_GFX_BULK_IN,
+                                                       (length & ~3),
+                                                       kernbuffer, NULL,
+                                                       bytes_read, 0);
+                               if (!ret) kernbuffer += (*bytes_read);
+                       }
+                       addr += (*bytes_read);
+                       length -= (*bytes_read);
+#endif
+           }
+
+           if (ret)
+               break;
+       }
+
+       return ret;
+}
+
+/* High level: Gfx (indexed) register access */
+
+static int
+sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data)
+{
+       int ret;
+       ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index);
+       ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data);
+       return ret;
+}
+
+static int
+sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data)
+{
+       int ret;
+       ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index);
+       ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data);
+       return ret;
+}
+
+static int
+sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,
+                                                       u8 myand, u8 myor)
+{
+       int ret;
+       u8 tmp;
+
+       ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx);
+       ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp);
+       tmp &= myand;
+       tmp |= myor;
+       ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp);
+       return ret;
+}
+
+static int
+sisusb_setidxregmask(struct sisusb_usb_data *sisusb, int port, u8 idx,
+                                                       u8 data, u8 mask)
+{
+       int ret;
+       u8 tmp;
+       ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx);
+       ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp);
+       tmp &= ~(mask);
+       tmp |= (data & mask);
+       ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp);
+       return ret;
+}
+
+static int
+sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor)
+{
+       return(sisusb_setidxregandor(sisusb, port, index, 0xff, myor));
+}
+
+static int
+sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand)
+{
+       return(sisusb_setidxregandor(sisusb, port, idx, myand, 0x00));
+}
+
+/* access pci config registers (reg numbers 0, 4, 8, etc) */
+
+static int
+sisusb_write_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 data)
+{
+       struct sisusb_packet packet;
+       int ret;
+
+       packet.header = 0x008f;
+       packet.address = regnum | 0x10000;
+       packet.data = data;
+       ret = sisusb_send_packet(sisusb, 10, &packet);
+       return ret;
+}
+
+static int
+sisusb_read_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 *data)
+{
+       struct sisusb_packet packet;
+       int ret;
+
+       packet.header = 0x008f;
+       packet.address = (u32)regnum | 0x10000;
+       ret = sisusb_send_packet(sisusb, 6, &packet);
+       *data = packet.data;
+       return ret;
+}
+
+/* Clear video RAM */
+
+static int
+sisusb_clear_vram(struct sisusb_usb_data *sisusb, u32 address, int length)
+{
+       int ret, i;
+       ssize_t j;
+
+       if (address < sisusb->vrambase)
+               return 1;
+
+       if (address >= sisusb->vrambase + sisusb->vramsize)
+               return 1;
+
+       if (address + length > sisusb->vrambase + sisusb->vramsize)
+               length = sisusb->vrambase + sisusb->vramsize - address;
+
+       if (length <= 0)
+               return 0;
+
+       /* allocate free buffer/urb and clear the buffer */
+       if ((i = sisusb_alloc_outbuf(sisusb)) < 0)
+               return -EBUSY;
+
+       memset(sisusb->obuf[i], 0, sisusb->obufsize);
+
+       /* We can write a length > buffer size here. The buffer
+        * data will simply be re-used (like a ring-buffer).
+        */
+       ret = sisusb_write_mem_bulk(sisusb, address, NULL, length, NULL, i, &j);
+
+       /* Free the buffer/urb */
+       sisusb_free_outbuf(sisusb, i);
+
+       return ret;
+}
+
+/* Initialize the graphics core (return 0 on success)
+ * This resets the graphics hardware and puts it into
+ * a defined mode (640x480@60Hz)
+ */
+
+#define GETREG(r,d)     sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, r, d)
+#define SETREG(r,d)    sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, r, d)
+#define SETIREG(r,i,d) sisusb_setidxreg(sisusb, r, i, d)
+#define GETIREG(r,i,d)  sisusb_getidxreg(sisusb, r, i, d)
+#define SETIREGOR(r,i,o)       sisusb_setidxregor(sisusb, r, i, o)
+#define SETIREGAND(r,i,a)      sisusb_setidxregand(sisusb, r, i, a)
+#define SETIREGANDOR(r,i,a,o)  sisusb_setidxregandor(sisusb, r, i, a, o)
+#define READL(a,d)     sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
+#define WRITEL(a,d)    sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d)
+#define READB(a,d)     sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
+#define WRITEB(a,d)    sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d)
+
+static int
+sisusb_triggersr16(struct sisusb_usb_data *sisusb, u8 ramtype)
+{
+       int ret;
+       u8 tmp8;
+
+       ret = GETIREG(SISSR, 0x16, &tmp8);
+       if (ramtype <= 1) {
+               tmp8 &= 0x3f;
+               ret |= SETIREG(SISSR, 0x16, tmp8);
+               tmp8 |= 0x80;
+               ret |= SETIREG(SISSR, 0x16, tmp8);
+       } else {
+               tmp8 |= 0xc0;
+               ret |= SETIREG(SISSR, 0x16, tmp8);
+               tmp8 &= 0x0f;
+               ret |= SETIREG(SISSR, 0x16, tmp8);
+               tmp8 |= 0x80;
+               ret |= SETIREG(SISSR, 0x16, tmp8);
+               tmp8 &= 0x0f;
+               ret |= SETIREG(SISSR, 0x16, tmp8);
+               tmp8 |= 0xd0;
+               ret |= SETIREG(SISSR, 0x16, tmp8);
+               tmp8 &= 0x0f;
+               ret |= SETIREG(SISSR, 0x16, tmp8);
+               tmp8 |= 0xa0;
+               ret |= SETIREG(SISSR, 0x16, tmp8);
+       }
+       return ret;
+}
+
+static int
+sisusb_getbuswidth(struct sisusb_usb_data *sisusb, int *bw, int *chab)
+{
+       int ret;
+       u8  ramtype, done = 0;
+       u32 t0, t1, t2, t3;
+       u32 ramptr = SISUSB_PCI_MEMBASE;
+
+       ret = GETIREG(SISSR, 0x3a, &ramtype);
+       ramtype &= 3;
+
+       ret |= SETIREG(SISSR, 0x13, 0x00);
+
+       if (ramtype <= 1) {
+               ret |= SETIREG(SISSR, 0x14, 0x12);
+               ret |= SETIREGAND(SISSR, 0x15, 0xef);
+       } else {
+               ret |= SETIREG(SISSR, 0x14, 0x02);
+       }
+
+       ret |= sisusb_triggersr16(sisusb, ramtype);
+       ret |= WRITEL(ramptr +  0, 0x01234567);
+       ret |= WRITEL(ramptr +  4, 0x456789ab);
+       ret |= WRITEL(ramptr +  8, 0x89abcdef);
+       ret |= WRITEL(ramptr + 12, 0xcdef0123);
+       ret |= WRITEL(ramptr + 16, 0x55555555);
+       ret |= WRITEL(ramptr + 20, 0x55555555);
+       ret |= WRITEL(ramptr + 24, 0xffffffff);
+       ret |= WRITEL(ramptr + 28, 0xffffffff);
+       ret |= READL(ramptr +  0, &t0);
+       ret |= READL(ramptr +  4, &t1);
+       ret |= READL(ramptr +  8, &t2);
+       ret |= READL(ramptr + 12, &t3);
+
+       if (ramtype <= 1) {
+
+               *chab = 0; *bw = 64;
+
+               if ((t3 != 0xcdef0123) || (t2 != 0x89abcdef)) {
+                       if ((t1 == 0x456789ab) && (t0 == 0x01234567)) {
+                               *chab = 0; *bw = 64;
+                               ret |= SETIREGAND(SISSR, 0x14, 0xfd);
+                       }
+               }
+               if ((t1 != 0x456789ab) || (t0 != 0x01234567)) {
+                       *chab = 1; *bw = 64;
+                       ret |= SETIREGANDOR(SISSR, 0x14, 0xfc,0x01);
+
+                       ret |= sisusb_triggersr16(sisusb, ramtype);
+                       ret |= WRITEL(ramptr +  0, 0x89abcdef);
+                       ret |= WRITEL(ramptr +  4, 0xcdef0123);
+                       ret |= WRITEL(ramptr +  8, 0x55555555);
+                       ret |= WRITEL(ramptr + 12, 0x55555555);
+                       ret |= WRITEL(ramptr + 16, 0xaaaaaaaa);
+                       ret |= WRITEL(ramptr + 20, 0xaaaaaaaa);
+                       ret |= READL(ramptr +  4, &t1);
+
+                       if (t1 != 0xcdef0123) {
+                               *bw = 32;
+                               ret |= SETIREGOR(SISSR, 0x15, 0x10);
+                       }
+               }
+
+       } else {
+
+               *chab = 0; *bw = 64;    /* default: cha, bw = 64 */
+
+               done = 0;
+
+               if (t1 == 0x456789ab) {
+                       if (t0 == 0x01234567) {
+                               *chab = 0; *bw = 64;
+                               done = 1;
+                       }
+               } else {
+                       if (t0 == 0x01234567) {
+                               *chab = 0; *bw = 32;
+                               ret |= SETIREG(SISSR, 0x14, 0x00);
+                               done = 1;
+                       }
+               }
+
+               if (!done) {
+                       ret |= SETIREG(SISSR, 0x14, 0x03);
+                       ret |= sisusb_triggersr16(sisusb, ramtype);
+
+                       ret |= WRITEL(ramptr +  0, 0x01234567);
+                       ret |= WRITEL(ramptr +  4, 0x456789ab);
+                       ret |= WRITEL(ramptr +  8, 0x89abcdef);
+                       ret |= WRITEL(ramptr + 12, 0xcdef0123);
+                       ret |= WRITEL(ramptr + 16, 0x55555555);
+                       ret |= WRITEL(ramptr + 20, 0x55555555);
+                       ret |= WRITEL(ramptr + 24, 0xffffffff);
+                       ret |= WRITEL(ramptr + 28, 0xffffffff);
+                       ret |= READL(ramptr +  0, &t0);
+                       ret |= READL(ramptr +  4, &t1);
+
+                       if (t1 == 0x456789ab) {
+                               if (t0 == 0x01234567) {
+                                       *chab = 1; *bw = 64;
+                                       return ret;
+                               } /* else error */
+                       } else {
+                               if (t0 == 0x01234567) {
+                                       *chab = 1; *bw = 32;
+                                       ret |= SETIREG(SISSR, 0x14, 0x01);
+                               } /* else error */
+                       }
+               }
+       }
+       return ret;
+}
+
+static int
+sisusb_verify_mclk(struct sisusb_usb_data *sisusb)
+{
+       int ret = 0;
+       u32 ramptr = SISUSB_PCI_MEMBASE;
+       u8 tmp1, tmp2, i, j;
+
+       ret |= WRITEB(ramptr, 0xaa);
+       ret |= WRITEB(ramptr + 16, 0x55);
+       ret |= READB(ramptr, &tmp1);
+       ret |= READB(ramptr + 16, &tmp2);
+       if ((tmp1 != 0xaa) || (tmp2 != 0x55)) {
+               for (i = 0, j = 16; i < 2; i++, j += 16) {
+                       ret |= GETIREG(SISSR, 0x21, &tmp1);
+                       ret |= SETIREGAND(SISSR, 0x21, (tmp1 & 0xfb));
+                       ret |= SETIREGOR(SISSR, 0x3c, 0x01);  /* not on 330 */
+                       ret |= SETIREGAND(SISSR, 0x3c, 0xfe); /* not on 330 */
+                       ret |= SETIREG(SISSR, 0x21, tmp1);
+                       ret |= WRITEB(ramptr + 16 + j, j);
+                       ret |= READB(ramptr + 16 + j, &tmp1);
+                       if (tmp1 == j) {
+                               ret |= WRITEB(ramptr + j, j);
+                               break;
+                       }
+               }
+       }
+       return ret;
+}
+
+static int
+sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret, int index,
+                       u8 rankno, u8 chab, const u8 dramtype[][5],
+                       int bw)
+{
+       int ret = 0, ranksize;
+       u8 tmp;
+
+       *iret = 0;
+
+       if ((rankno == 2) && (dramtype[index][0] == 2))
+               return ret;
+
+       ranksize = dramtype[index][3] / 2 * bw / 32;
+
+       if ((ranksize * rankno) > 128)
+               return ret;
+
+       tmp = 0;
+       while ((ranksize >>= 1) > 0) tmp += 0x10;
+       tmp |= ((rankno - 1) << 2);
+       tmp |= ((bw / 64) & 0x02);
+       tmp |= (chab & 0x01);
+
+       ret = SETIREG(SISSR, 0x14, tmp);
+       ret |= sisusb_triggersr16(sisusb, 0); /* sic! */
+
+       *iret = 1;
+
+       return ret;
+}
+
+static int
+sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret, u32 inc, int testn)
+{
+       int ret = 0, i;
+       u32 j, tmp;
+
+       *iret = 0;
+
+       for (i = 0, j = 0; i < testn; i++) {
+               ret |= WRITEL(sisusb->vrambase + j, j);
+               j += inc;
+       }
+
+       for (i = 0, j = 0; i < testn; i++) {
+               ret |= READL(sisusb->vrambase + j, &tmp);
+               if (tmp != j) return ret;
+               j += inc;
+       }
+
+       *iret = 1;
+       return ret;
+}
+
+static int
+sisusb_check_ranks(struct sisusb_usb_data *sisusb, int *iret, int rankno,
+                                       int idx, int bw, const u8 rtype[][5])
+{
+       int ret = 0, i, i2ret;
+       u32 inc;
+
+       *iret = 0;
+
+       for (i = rankno; i >= 1; i--) {
+               inc = 1 << (rtype[idx][2] +
+                           rtype[idx][1] +
+                           rtype[idx][0] +
+                           bw / 64 + i);
+               ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2);
+               if (!i2ret)
+                       return ret;
+       }
+
+       inc = 1 << (rtype[idx][2] + bw / 64 + 2);
+       ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 4);
+       if (!i2ret)
+               return ret;
+
+       inc = 1 << (10 + bw / 64);
+       ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2);
+       if (!i2ret)
+               return ret;
+
+       *iret = 1;
+       return ret;
+}
+
+static int
+sisusb_get_sdram_size(struct sisusb_usb_data *sisusb, int *iret, int bw,
+                                                               int chab)
+{
+       int ret = 0, i2ret = 0, i, j;
+       static const u8 sdramtype[13][5] = {
+               { 2, 12, 9, 64, 0x35 },
+               { 1, 13, 9, 64, 0x44 },
+               { 2, 12, 8, 32, 0x31 },
+               { 2, 11, 9, 32, 0x25 },
+               { 1, 12, 9, 32, 0x34 },
+               { 1, 13, 8, 32, 0x40 },
+               { 2, 11, 8, 16, 0x21 },
+               { 1, 12, 8, 16, 0x30 },
+               { 1, 11, 9, 16, 0x24 },
+               { 1, 11, 8,  8, 0x20 },
+               { 2,  9, 8,  4, 0x01 },
+               { 1, 10, 8,  4, 0x10 },
+               { 1,  9, 8,  2, 0x00 }
+       };
+
+       *iret = 1; /* error */
+
+       for (i = 0; i < 13; i++) {
+               ret |= SETIREGANDOR(SISSR, 0x13, 0x80, sdramtype[i][4]);
+               for (j = 2; j > 0; j--) {
+                       ret |= sisusb_set_rank(sisusb, &i2ret, i, j,
+                                               chab, sdramtype, bw);
+                       if (!i2ret)
+                               continue;
+
+                       ret |= sisusb_check_ranks(sisusb, &i2ret, j, i,
+                                               bw, sdramtype);
+                       if (i2ret) {
+                               *iret = 0;      /* ram size found */
+                               return ret;
+                       }
+               }
+       }
+
+       return ret;
+}
+
+static int
+sisusb_setup_screen(struct sisusb_usb_data *sisusb, int clrall, int drwfr)
+{
+       int ret = 0;
+       u32 address;
+       int i, length, modex, modey, bpp;
+
+       modex = 640; modey = 480; bpp = 2;
+
+       address = sisusb->vrambase;     /* Clear video ram */
+
+       if (clrall)
+               length = sisusb->vramsize;
+       else
+               length = modex * bpp * modey;
+
+       ret = sisusb_clear_vram(sisusb, address, length);
+
+       if (!ret && drwfr) {
+               for (i = 0; i < modex; i++) {
+                       address = sisusb->vrambase + (i * bpp);
+                       ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
+                                                       address, 0xf100);
+                       address += (modex * (modey-1) * bpp);
+                       ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
+                                                       address, 0xf100);
+               }
+               for (i = 0; i < modey; i++) {
+                       address = sisusb->vrambase + ((i * modex) * bpp);
+                       ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
+                                                       address, 0xf100);
+                       address += ((modex - 1) * bpp);
+                       ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM,
+                                                       address, 0xf100);
+               }
+       }
+
+       return ret;
+}
+
+static int
+sisusb_set_default_mode(struct sisusb_usb_data *sisusb, int touchengines)
+{
+       int ret = 0, i, j, modex, modey, bpp, du;
+       u8 sr31, cr63, tmp8;
+       static const char attrdata[] = {
+               0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
+               0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
+               0x01,0x00,0x00,0x00
+       };
+       static const char crtcrdata[] = {
+               0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e,
+               0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,
+               0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3,
+               0xff
+       };
+       static const char grcdata[] = {
+               0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f,
+               0xff
+       };
+       static const char crtcdata[] = {
+               0x5f,0x4f,0x4f,0x83,0x55,0x81,0x0b,0x3e,
+               0xe9,0x8b,0xdf,0xe8,0x0c,0x00,0x00,0x05,
+               0x00
+       };
+
+       modex = 640; modey = 480; bpp = 2;
+
+       GETIREG(SISSR, 0x31, &sr31);
+       GETIREG(SISCR, 0x63, &cr63);
+       SETIREGOR(SISSR, 0x01, 0x20);
+       SETIREG(SISCR, 0x63, cr63 & 0xbf);
+       SETIREGOR(SISCR, 0x17, 0x80);
+       SETIREGOR(SISSR, 0x1f, 0x04);
+       SETIREGAND(SISSR, 0x07, 0xfb);
+       SETIREG(SISSR, 0x00, 0x03);     /* seq */
+       SETIREG(SISSR, 0x01, 0x21);
+       SETIREG(SISSR, 0x02, 0x0f);
+       SETIREG(SISSR, 0x03, 0x00);
+       SETIREG(SISSR, 0x04, 0x0e);
+       SETREG(SISMISCW, 0x23);         /* misc */
+       for (i = 0; i <= 0x18; i++) {   /* crtc */
+               SETIREG(SISCR, i, crtcrdata[i]);
+       }
+       for (i = 0; i <= 0x13; i++) {   /* att */
+               GETREG(SISINPSTAT, &tmp8);
+               SETREG(SISAR, i);
+               SETREG(SISAR, attrdata[i]);
+       }
+       GETREG(SISINPSTAT, &tmp8);
+       SETREG(SISAR, 0x14);
+       SETREG(SISAR, 0x00);
+       GETREG(SISINPSTAT, &tmp8);
+       SETREG(SISAR, 0x20);
+       GETREG(SISINPSTAT, &tmp8);
+       for (i = 0; i <= 0x08; i++) {   /* grc */
+               SETIREG(SISGR, i, grcdata[i]);
+       }
+       SETIREGAND(SISGR, 0x05, 0xbf);
+       for (i = 0x0A; i <= 0x0E; i++) {        /* clr ext */
+               SETIREG(SISSR, i, 0x00);
+       }
+       SETIREGAND(SISSR, 0x37, 0xfe);
+       SETREG(SISMISCW, 0xef);         /* sync */
+       SETIREG(SISCR, 0x11, 0x00);     /* crtc */
+       for (j = 0x00, i = 0; i <= 7; i++, j++) {
+               SETIREG(SISCR, j, crtcdata[i]);
+       }
+       for (j = 0x10; i <= 10; i++, j++) {
+               SETIREG(SISCR, j, crtcdata[i]);
+       }
+       for (j = 0x15; i <= 12; i++, j++) {
+               SETIREG(SISCR, j, crtcdata[i]);
+       }
+       for (j = 0x0A; i <= 15; i++, j++) {
+               SETIREG(SISSR, j, crtcdata[i]);
+       }
+       SETIREG(SISSR, 0x0E, (crtcdata[16] & 0xE0));
+       SETIREGANDOR(SISCR, 0x09, 0x5f, ((crtcdata[16] & 0x01) << 5));
+       SETIREG(SISCR, 0x14, 0x4f);
+       du = (modex / 16) * (bpp * 2);  /* offset/pitch */
+       if (modex % 16) du += bpp;
+       SETIREGANDOR(SISSR, 0x0e, 0xf0, ((du >> 8) & 0x0f));
+       SETIREG(SISCR, 0x13, (du & 0xff));
+       du <<= 5;
+       tmp8 = du >> 8;
+       if (du & 0xff) tmp8++;
+       SETIREG(SISSR, 0x10, tmp8);
+       SETIREG(SISSR, 0x31, 0x00);     /* VCLK */
+       SETIREG(SISSR, 0x2b, 0x1b);
+       SETIREG(SISSR, 0x2c, 0xe1);
+       SETIREG(SISSR, 0x2d, 0x01);
+       SETIREGAND(SISSR, 0x3d, 0xfe);  /* FIFO */
+       SETIREG(SISSR, 0x08, 0xae);
+       SETIREGAND(SISSR, 0x09, 0xf0);
+       SETIREG(SISSR, 0x08, 0x34);
+       SETIREGOR(SISSR, 0x3d, 0x01);
+       SETIREGAND(SISSR, 0x1f, 0x3f);  /* mode regs */
+       SETIREGANDOR(SISSR, 0x06, 0xc0, 0x0a);
+       SETIREG(SISCR, 0x19, 0x00);
+       SETIREGAND(SISCR, 0x1a, 0xfc);
+       SETIREGAND(SISSR, 0x0f, 0xb7);
+       SETIREGAND(SISSR, 0x31, 0xfb);
+       SETIREGANDOR(SISSR, 0x21, 0x1f, 0xa0);
+       SETIREGAND(SISSR, 0x32, 0xf3);
+       SETIREGANDOR(SISSR, 0x07, 0xf8, 0x03);
+       SETIREG(SISCR, 0x52, 0x6c);
+
+       SETIREG(SISCR, 0x0d, 0x00);     /* adjust frame */
+       SETIREG(SISCR, 0x0c, 0x00);
+       SETIREG(SISSR, 0x0d, 0x00);
+       SETIREGAND(SISSR, 0x37, 0xfe);
+
+       SETIREG(SISCR, 0x32, 0x20);
+       SETIREGAND(SISSR, 0x01, 0xdf);  /* enable display */
+       SETIREG(SISCR, 0x63, (cr63 & 0xbf));
+       SETIREG(SISSR, 0x31, (sr31 & 0xfb));
+
+       if (touchengines) {
+               SETIREG(SISSR, 0x20, 0xa1);     /* enable engines */
+               SETIREGOR(SISSR, 0x1e, 0x5a);
+
+               SETIREG(SISSR, 0x26, 0x01);     /* disable cmdqueue */
+               SETIREG(SISSR, 0x27, 0x1f);
+               SETIREG(SISSR, 0x26, 0x00);
+       }
+
+       SETIREG(SISCR, 0x34, 0x44);     /* we just set std mode #44 */
+
+       return ret;
+}
+
+static int
+sisusb_init_gfxcore(struct sisusb_usb_data *sisusb)
+{
+       int ret = 0, i, j, bw, chab, iret, retry = 3;
+       u8 tmp8, ramtype;
+       u32 tmp32;
+       static const char mclktable[] = {
+               0x3b, 0x22, 0x01, 143,
+               0x3b, 0x22, 0x01, 143,
+               0x3b, 0x22, 0x01, 143,
+               0x3b, 0x22, 0x01, 143
+       };
+       static const char eclktable[] = {
+               0x3b, 0x22, 0x01, 143,
+               0x3b, 0x22, 0x01, 143,
+               0x3b, 0x22, 0x01, 143,
+               0x3b, 0x22, 0x01, 143
+       };
+       static const char ramtypetable1[] = {
+               0x00, 0x04, 0x60, 0x60,
+               0x0f, 0x0f, 0x1f, 0x1f,
+               0xba, 0xba, 0xba, 0xba,
+               0xa9, 0xa9, 0xac, 0xac,
+               0xa0, 0xa0, 0xa0, 0xa8,
+               0x00, 0x00, 0x02, 0x02,
+               0x30, 0x30, 0x40, 0x40
+       };
+       static const char ramtypetable2[] = {
+               0x77, 0x77, 0x44, 0x44,
+               0x77, 0x77, 0x44, 0x44,
+               0x00, 0x00, 0x00, 0x00,
+               0x5b, 0x5b, 0xab, 0xab,
+               0x00, 0x00, 0xf0, 0xf8
+       };
+
+       while (retry--) {
+
+               /* Enable VGA */
+               ret = GETREG(SISVGAEN, &tmp8);
+               ret |= SETREG(SISVGAEN, (tmp8 | 0x01));
+
+               /* Enable GPU access to VRAM */
+               ret |= GETREG(SISMISCR, &tmp8);
+               ret |= SETREG(SISMISCW, (tmp8 | 0x01));
+
+               if (ret) continue;
+
+               /* Reset registers */
+               ret |= SETIREGAND(SISCR, 0x5b, 0xdf);
+               ret |= SETIREG(SISSR, 0x05, 0x86);
+               ret |= SETIREGOR(SISSR, 0x20, 0x01);
+
+               ret |= SETREG(SISMISCW, 0x67);
+
+               for (i = 0x06; i <= 0x1f; i++) {
+                       ret |= SETIREG(SISSR, i, 0x00);
+               }
+               for (i = 0x21; i <= 0x27; i++) {
+                       ret |= SETIREG(SISSR, i, 0x00);
+               }
+               for (i = 0x31; i <= 0x3d; i++) {
+                       ret |= SETIREG(SISSR, i, 0x00);
+               }
+               for (i = 0x12; i <= 0x1b; i++) {
+                       ret |= SETIREG(SISSR, i, 0x00);
+               }
+               for (i = 0x79; i <= 0x7c; i++) {
+                       ret |= SETIREG(SISCR, i, 0x00);
+               }
+
+               if (ret) continue;
+
+               ret |= SETIREG(SISCR, 0x63, 0x80);
+
+               ret |= GETIREG(SISSR, 0x3a, &ramtype);
+               ramtype &= 0x03;
+
+               ret |= SETIREG(SISSR, 0x28, mclktable[ramtype * 4]);
+               ret |= SETIREG(SISSR, 0x29, mclktable[(ramtype * 4) + 1]);
+               ret |= SETIREG(SISSR, 0x2a, mclktable[(ramtype * 4) + 2]);
+
+               ret |= SETIREG(SISSR, 0x2e, eclktable[ramtype * 4]);
+               ret |= SETIREG(SISSR, 0x2f, eclktable[(ramtype * 4) + 1]);
+               ret |= SETIREG(SISSR, 0x30, eclktable[(ramtype * 4) + 2]);
+
+               ret |= SETIREG(SISSR, 0x07, 0x18);
+               ret |= SETIREG(SISSR, 0x11, 0x0f);
+
+               if (ret) continue;
+
+               for (i = 0x15, j = 0; i <= 0x1b; i++, j++) {
+                       ret |= SETIREG(SISSR, i, ramtypetable1[(j*4) + ramtype]);
+               }
+               for (i = 0x40, j = 0; i <= 0x44; i++, j++) {
+                       ret |= SETIREG(SISCR, i, ramtypetable2[(j*4) + ramtype]);
+               }
+
+               ret |= SETIREG(SISCR, 0x49, 0xaa);
+
+               ret |= SETIREG(SISSR, 0x1f, 0x00);
+               ret |= SETIREG(SISSR, 0x20, 0xa0);
+               ret |= SETIREG(SISSR, 0x23, 0xf6);
+               ret |= SETIREG(SISSR, 0x24, 0x0d);
+               ret |= SETIREG(SISSR, 0x25, 0x33);
+
+               ret |= SETIREG(SISSR, 0x11, 0x0f);
+
+               ret |= SETIREGOR(SISPART1, 0x2f, 0x01);
+
+               ret |= SETIREGAND(SISCAP, 0x3f, 0xef);
+
+               if (ret) continue;
+
+               ret |= SETIREG(SISPART1, 0x00, 0x00);
+
+               ret |= GETIREG(SISSR, 0x13, &tmp8);
+               tmp8 >>= 4;
+
+               ret |= SETIREG(SISPART1, 0x02, 0x00);
+               ret |= SETIREG(SISPART1, 0x2e, 0x08);
+
+               ret |= sisusb_read_pci_config(sisusb, 0x50, &tmp32);
+               tmp32 &= 0x00f00000;
+               tmp8 = (tmp32 == 0x100000) ? 0x33 : 0x03;
+               ret |= SETIREG(SISSR, 0x25, tmp8);
+               tmp8 = (tmp32 == 0x100000) ? 0xaa : 0x88;
+               ret |= SETIREG(SISCR, 0x49, tmp8);
+
+               ret |= SETIREG(SISSR, 0x27, 0x1f);
+               ret |= SETIREG(SISSR, 0x31, 0x00);
+               ret |= SETIREG(SISSR, 0x32, 0x11);
+               ret |= SETIREG(SISSR, 0x33, 0x00);
+
+               if (ret) continue;
+
+               ret |= SETIREG(SISCR, 0x83, 0x00);
+
+               ret |= sisusb_set_default_mode(sisusb, 0);
+
+               ret |= SETIREGAND(SISSR, 0x21, 0xdf);
+               ret |= SETIREGOR(SISSR, 0x01, 0x20);
+               ret |= SETIREGOR(SISSR, 0x16, 0x0f);
+
+               ret |= sisusb_triggersr16(sisusb, ramtype);
+
+               /* Disable refresh */
+               ret |= SETIREGAND(SISSR, 0x17, 0xf8);
+               ret |= SETIREGOR(SISSR, 0x19, 0x03);
+
+               ret |= sisusb_getbuswidth(sisusb, &bw, &chab);
+               ret |= sisusb_verify_mclk(sisusb);
+
+               if (ramtype <= 1) {
+                       ret |= sisusb_get_sdram_size(sisusb, &iret, bw, chab);
+                       if (iret) {
+                               printk(KERN_ERR "sisusbvga[%d]: RAM size "
+                                       "detection failed, "
+                                       "assuming 8MB video RAM\n",
+                                       sisusb->minor);
+                               ret |= SETIREG(SISSR,0x14,0x31);
+                               /* TODO */
+                       }
+               } else {
+                       printk(KERN_ERR "sisusbvga[%d]: DDR RAM device found, "
+                                       "assuming 8MB video RAM\n",
+                                       sisusb->minor);
+                       ret |= SETIREG(SISSR,0x14,0x31);
+                       /* *** TODO *** */
+               }
+
+               /* Enable refresh */
+               ret |= SETIREG(SISSR, 0x16, ramtypetable1[4 + ramtype]);
+               ret |= SETIREG(SISSR, 0x17, ramtypetable1[8 + ramtype]);
+               ret |= SETIREG(SISSR, 0x19, ramtypetable1[16 + ramtype]);
+
+               ret |= SETIREGOR(SISSR, 0x21, 0x20);
+
+               ret |= SETIREG(SISSR, 0x22, 0xfb);
+               ret |= SETIREG(SISSR, 0x21, 0xa5);
+
+               if (ret == 0)
+                       break;
+       }
+
+       return ret;
+}
+
+#undef SETREG
+#undef GETREG
+#undef SETIREG
+#undef GETIREG
+#undef SETIREGOR
+#undef SETIREGAND
+#undef SETIREGANDOR
+#undef READL
+#undef WRITEL
+
+static void
+sisusb_get_ramconfig(struct sisusb_usb_data *sisusb)
+{
+       u8 tmp8, tmp82, ramtype;
+       int bw = 0;
+       char *ramtypetext1 = NULL;
+       const char *ramtypetext2[] = {  "SDR SDRAM", "SDR SGRAM",
+                                       "DDR SDRAM", "DDR SGRAM" };
+       static const int busSDR[4]  = {64, 64, 128, 128};
+       static const int busDDR[4]  = {32, 32,  64,  64};
+       static const int busDDRA[4] = {64+32, 64+32 , (64+32)*2, (64+32)*2};
+
+       sisusb_getidxreg(sisusb, SISSR, 0x14, &tmp8);
+       sisusb_getidxreg(sisusb, SISSR, 0x15, &tmp82);
+       sisusb_getidxreg(sisusb, SISSR, 0x3a, &ramtype);
+       sisusb->vramsize = (1 << ((tmp8 & 0xf0) >> 4)) * 1024 * 1024;
+       ramtype &= 0x03;
+       switch ((tmp8 >> 2) & 0x03) {
+       case 0: ramtypetext1 = "1 ch/1 r";
+               if (tmp82 & 0x10) {
+                       bw = 32;
+               } else {
+                       bw = busSDR[(tmp8 & 0x03)];
+               }
+               break;
+       case 1: ramtypetext1 = "1 ch/2 r";
+               sisusb->vramsize <<= 1;
+               bw = busSDR[(tmp8 & 0x03)];
+               break;
+       case 2: ramtypetext1 = "asymmeric";
+               sisusb->vramsize += sisusb->vramsize/2;
+               bw = busDDRA[(tmp8 & 0x03)];
+               break;
+       case 3: ramtypetext1 = "2 channel";
+               sisusb->vramsize <<= 1;
+               bw = busDDR[(tmp8 & 0x03)];
+               break;
+       }
+
+       printk(KERN_INFO "sisusbvga[%d]: %dMB %s %s, bus width %d\n",
+                       sisusb->minor, (sisusb->vramsize >> 20), ramtypetext1,
+                       ramtypetext2[ramtype], bw);
+}
+
+static int
+sisusb_do_init_gfxdevice(struct sisusb_usb_data *sisusb)
+{
+       struct sisusb_packet packet;
+       int ret;
+       u32 tmp32;
+
+       /* Do some magic */
+       packet.header  = 0x001f;
+       packet.address = 0x00000324;
+       packet.data    = 0x00000004;
+       ret = sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+       packet.header  = 0x001f;
+       packet.address = 0x00000364;
+       packet.data    = 0x00000004;
+       ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+       packet.header  = 0x001f;
+       packet.address = 0x00000384;
+       packet.data    = 0x00000004;
+       ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+       packet.header  = 0x001f;
+       packet.address = 0x00000100;
+       packet.data    = 0x00000700;
+       ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+       packet.header  = 0x000f;
+       packet.address = 0x00000004;
+       ret |= sisusb_send_bridge_packet(sisusb, 6, &packet, 0);
+       packet.data |= 0x17;
+       ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+
+       /* Init BAR 0 (VRAM) */
+       ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
+       ret |= sisusb_write_pci_config(sisusb, 0x10, 0xfffffff0);
+       ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
+       tmp32 &= 0x0f;
+       tmp32 |= SISUSB_PCI_MEMBASE;
+       ret |= sisusb_write_pci_config(sisusb, 0x10, tmp32);
+
+       /* Init BAR 1 (MMIO) */
+       ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
+       ret |= sisusb_write_pci_config(sisusb, 0x14, 0xfffffff0);
+       ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
+       tmp32 &= 0x0f;
+       tmp32 |= SISUSB_PCI_MMIOBASE;
+       ret |= sisusb_write_pci_config(sisusb, 0x14, tmp32);
+
+       /* Init BAR 2 (i/o ports) */
+       ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
+       ret |= sisusb_write_pci_config(sisusb, 0x18, 0xfffffff0);
+       ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
+       tmp32 &= 0x0f;
+       tmp32 |= SISUSB_PCI_IOPORTBASE;
+       ret |= sisusb_write_pci_config(sisusb, 0x18, tmp32);
+
+       /* Enable memory and i/o access */
+       ret |= sisusb_read_pci_config(sisusb, 0x04, &tmp32);
+       tmp32 |= 0x3;
+       ret |= sisusb_write_pci_config(sisusb, 0x04, tmp32);
+
+       if (ret == 0) {
+               /* Some further magic */
+               packet.header  = 0x001f;
+               packet.address = 0x00000050;
+               packet.data    = 0x000000ff;
+               ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0);
+       }
+
+       return ret;
+}
+
+/* Initialize the graphics device (return 0 on success)
+ * This initializes the net2280 as well as the PCI registers
+ * of the graphics board.
+ */
+
+static int
+sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen)
+{
+       int ret = 0, test = 0;
+       u32 tmp32;
+
+       if (sisusb->devinit == 1) {
+               /* Read PCI BARs and see if they have been set up */
+               ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32);
+               if (ret) return ret;
+               if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MEMBASE) test++;
+
+               ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32);
+               if (ret) return ret;
+               if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MMIOBASE) test++;
+
+               ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32);
+               if (ret) return ret;
+               if ((tmp32 & 0xfffffff0) == SISUSB_PCI_IOPORTBASE) test++;
+       }
+
+       /* No? So reset the device */
+       if ((sisusb->devinit == 0) || (test != 3)) {
+
+               ret |= sisusb_do_init_gfxdevice(sisusb);
+
+               if (ret == 0)
+                       sisusb->devinit = 1;
+
+       }
+
+       if (sisusb->devinit) {
+               /* Initialize the graphics core */
+               if (sisusb_init_gfxcore(sisusb) == 0) {
+                       sisusb->gfxinit = 1;
+                       sisusb_get_ramconfig(sisusb);
+                       ret |= sisusb_set_default_mode(sisusb, 1);
+                       ret |= sisusb_setup_screen(sisusb, 1, initscreen);
+               }
+       }
+
+       return ret;
+}
+
+/* fops */
+
+static int
+sisusb_open(struct inode *inode, struct file *file)
+{
+       struct sisusb_usb_data *sisusb;
+       struct usb_interface *interface;
+       int subminor = iminor(inode);
+
+       down(&disconnect_sem);
+
+       if (!(interface = usb_find_interface(&sisusb_driver, subminor))) {
+               printk(KERN_ERR "sisusb[%d]: Failed to find interface\n",
+                               subminor);
+               up(&disconnect_sem);
+               return -ENODEV;
+       }
+
+       if (!(sisusb = usb_get_intfdata(interface))) {
+               up(&disconnect_sem);
+               return -ENODEV;
+       }
+
+       down(&sisusb->lock);
+
+       if (!sisusb->present || !sisusb->ready) {
+               up(&sisusb->lock);
+               up(&disconnect_sem);
+               return -ENODEV;
+       }
+
+       if (sisusb->isopen) {
+               up(&sisusb->lock);
+               up(&disconnect_sem);
+               return -EBUSY;
+       }
+
+       if (!sisusb->devinit) {
+               if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) {
+                       if (sisusb_init_gfxdevice(sisusb, 0)) {
+                               up(&sisusb->lock);
+                               up(&disconnect_sem);
+                               printk(KERN_ERR
+                                       "sisusbvga[%d]: Failed to initialize "
+                                       "device\n",
+                                       sisusb->minor);
+                               return -EIO;
+                       }
+               } else {
+                       up(&sisusb->lock);
+                       up(&disconnect_sem);
+                       printk(KERN_ERR
+                               "sisusbvga[%d]: Device not attached to "
+                               "USB 2.0 hub\n",
+                               sisusb->minor);
+                       return -EIO;
+               }
+       }
+
+       /* increment usage count for the device */
+       kref_get(&sisusb->kref);
+
+       sisusb->isopen = 1;
+
+       file->private_data = sisusb;
+
+       up(&sisusb->lock);
+
+       up(&disconnect_sem);
+
+       printk(KERN_DEBUG "sisusbvga[%d]: opened", sisusb->minor);
+
+       return 0;
+}
+
+static void
+sisusb_delete(struct kref *kref)
+{
+       struct sisusb_usb_data *sisusb = to_sisusb_dev(kref);
+
+       if (!sisusb)
+               return;
+
+       if (sisusb->sisusb_dev)
+               usb_put_dev(sisusb->sisusb_dev);
+
+       sisusb->sisusb_dev = NULL;
+       sisusb_free_buffers(sisusb);
+       sisusb_free_urbs(sisusb);
+       kfree(sisusb);
+}
+
+static int
+sisusb_release(struct inode *inode, struct file *file)
+{
+       struct sisusb_usb_data *sisusb;
+       int myminor;
+
+       down(&disconnect_sem);
+
+       if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) {
+               up(&disconnect_sem);
+               return -ENODEV;
+       }
+
+       down(&sisusb->lock);
+
+       if (sisusb->present) {
+               /* Wait for all URBs to finish if device still present */
+               if (!sisusb_wait_all_out_complete(sisusb))
+                       sisusb_kill_all_busy(sisusb);
+       }
+
+       myminor = sisusb->minor;
+
+       sisusb->isopen = 0;
+       file->private_data = NULL;
+
+       up(&sisusb->lock);
+
+       /* decrement the usage count on our device */
+       kref_put(&sisusb->kref, sisusb_delete);
+
+       up(&disconnect_sem);
+
+       printk(KERN_DEBUG "sisusbvga[%d]: released", myminor);
+
+       return 0;
+}
+
+static ssize_t
+sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+{
+       struct sisusb_usb_data *sisusb;
+       ssize_t bytes_read = 0;
+       int errno = 0;
+       u8 buf8;
+       u16 buf16;
+       u32 buf32, address;
+
+       if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+               return -ENODEV;
+
+       down(&sisusb->lock);
+
+       /* Sanity check */
+       if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
+               up(&sisusb->lock);
+               return -ENODEV;
+       }
+
+       if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
+           (*ppos) <  SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
+
+               address = (*ppos) -
+                       SISUSB_PCI_PSEUDO_IOPORTBASE +
+                       SISUSB_PCI_IOPORTBASE;
+
+               /* Read i/o ports
+                * Byte, word and long(32) can be read. As this
+                * emulates inX instructions, the data returned is
+                * in machine-endianness.
+                */
+               switch (count) {
+
+                       case 1:
+                               if (sisusb_read_memio_byte(sisusb,
+                                                       SISUSB_TYPE_IO,
+                                                       address, &buf8))
+                                       errno = -EIO;
+                               else if (put_user(buf8, (u8 __user *)buffer))
+                                       errno = -EFAULT;
+                               else
+                                       bytes_read = 1;
+
+                               break;
+
+                       case 2:
+                               if (sisusb_read_memio_word(sisusb,
+                                                       SISUSB_TYPE_IO,
+                                                       address, &buf16))
+                                       errno = -EIO;
+                               else if (put_user(buf16, (u16 __user *)buffer))
+                                       errno = -EFAULT;
+                               else
+                                       bytes_read = 2;
+
+                               break;
+
+                       case 4:
+                               if (sisusb_read_memio_long(sisusb,
+                                                       SISUSB_TYPE_IO,
+                                                       address, &buf32))
+                                       errno = -EIO;
+                               else if (put_user(buf32, (u32 __user *)buffer))
+                                       errno = -EFAULT;
+                               else
+                                       bytes_read = 4;
+
+                               break;
+
+                       default:
+                               errno = -EIO;
+
+               }
+
+       } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
+                  (*ppos) <  SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
+
+               address = (*ppos) -
+                       SISUSB_PCI_PSEUDO_MEMBASE +
+                       SISUSB_PCI_MEMBASE;
+
+               /* Read video ram
+                * Remember: Data delivered is never endian-corrected
+                */
+               errno = sisusb_read_mem_bulk(sisusb, address,
+                                       NULL, count, buffer, &bytes_read);
+
+               if (bytes_read)
+                       errno = bytes_read;
+
+       } else  if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
+                   (*ppos) <  SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) {
+
+               address = (*ppos) -
+                       SISUSB_PCI_PSEUDO_MMIOBASE +
+                       SISUSB_PCI_MMIOBASE;
+
+               /* Read MMIO
+                * Remember: Data delivered is never endian-corrected
+                */
+               errno = sisusb_read_mem_bulk(sisusb, address,
+                                       NULL, count, buffer, &bytes_read);
+
+               if (bytes_read)
+                       errno = bytes_read;
+
+       } else  if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
+                   (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + 0x5c) {
+
+               if (count != 4) {
+                       up(&sisusb->lock);
+                       return -EINVAL;
+               }
+
+               address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
+
+               /* Read PCI config register
+                * Return value delivered in machine endianness.
+                */
+               if (sisusb_read_pci_config(sisusb, address, &buf32))
+                       errno = -EIO;
+               else if (put_user(buf32, (u32 __user *)buffer))
+                       errno = -EFAULT;
+               else
+                       bytes_read = 4;
+
+       } else {
+
+               errno = -EBADFD;
+
+       }
+
+       (*ppos) += bytes_read;
+
+       up(&sisusb->lock);
+
+       return errno ? errno : bytes_read;
+}
+
+static ssize_t
+sisusb_write(struct file *file, const char __user *buffer, size_t count,
+                                                               loff_t *ppos)
+{
+       struct sisusb_usb_data *sisusb;
+       int errno = 0;
+       ssize_t bytes_written = 0;
+       u8 buf8;
+       u16 buf16;
+       u32 buf32, address;
+
+       if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+               return -ENODEV;
+
+       down(&sisusb->lock);
+
+       /* Sanity check */
+       if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
+               up(&sisusb->lock);
+               return -ENODEV;
+       }
+
+       if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE &&
+           (*ppos) <  SISUSB_PCI_PSEUDO_IOPORTBASE + 128) {
+
+               address = (*ppos) -
+                       SISUSB_PCI_PSEUDO_IOPORTBASE +
+                       SISUSB_PCI_IOPORTBASE;
+
+               /* Write i/o ports
+                * Byte, word and long(32) can be written. As this
+                * emulates outX instructions, the data is expected
+                * in machine-endianness.
+                */
+               switch (count) {
+
+                       case 1:
+                               if (get_user(buf8, (u8 __user *)buffer))
+                                       errno = -EFAULT;
+                               else if (sisusb_write_memio_byte(sisusb,
+                                                       SISUSB_TYPE_IO,
+                                                       address, buf8))
+                                       errno = -EIO;
+                               else
+                                       bytes_written = 1;
+
+                               break;
+
+                       case 2:
+                               if (get_user(buf16, (u16 __user *)buffer))
+                                       errno = -EFAULT;
+                               else if (sisusb_write_memio_word(sisusb,
+                                                       SISUSB_TYPE_IO,
+                                                       address, buf16))
+                                       errno = -EIO;
+                               else
+                                       bytes_written = 2;
+
+                               break;
+
+                       case 4:
+                               if (get_user(buf32, (u32 __user *)buffer))
+                                       errno = -EFAULT;
+                               else if (sisusb_write_memio_long(sisusb,
+                                                       SISUSB_TYPE_IO,
+                                                       address, buf32))
+                                       errno = -EIO;
+                               else
+                                       bytes_written = 4;
+
+                               break;
+
+                       default:
+                               errno = -EIO;
+               }
+
+       } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE &&
+                  (*ppos) <  SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) {
+
+               address = (*ppos) -
+                       SISUSB_PCI_PSEUDO_MEMBASE +
+                       SISUSB_PCI_MEMBASE;
+
+               /* Write video ram.
+                * Buffer is copied 1:1, therefore, on big-endian
+                * machines, the data must be swapped by userland
+                * in advance (if applicable; no swapping in 8bpp
+                * mode or if YUV data is being transferred).
+                */
+               errno = sisusb_write_mem_bulk(sisusb, address, NULL,
+                                       count, buffer, 0, &bytes_written);
+
+               if (bytes_written)
+                       errno = bytes_written;
+
+       } else  if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE &&
+                   (*ppos) <  SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) {
+
+               address = (*ppos) -
+                       SISUSB_PCI_PSEUDO_MMIOBASE +
+                       SISUSB_PCI_MMIOBASE;
+
+               /* Write MMIO.
+                * Buffer is copied 1:1, therefore, on big-endian
+                * machines, the data must be swapped by userland
+                * in advance.
+                */
+               errno = sisusb_write_mem_bulk(sisusb, address, NULL,
+                                       count, buffer, 0, &bytes_written);
+
+               if (bytes_written)
+                       errno = bytes_written;
+
+       } else  if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE &&
+                   (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + SISUSB_PCI_PCONFSIZE) {
+
+               if (count != 4) {
+                       up(&sisusb->lock);
+                       return -EINVAL;
+               }
+
+               address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE;
+
+               /* Write PCI config register.
+                * Given value expected in machine endianness.
+                */
+               if (get_user(buf32, (u32 __user *)buffer))
+                       errno = -EFAULT;
+               else if (sisusb_write_pci_config(sisusb, address, buf32))
+                       errno = -EIO;
+               else
+                       bytes_written = 4;
+
+
+       } else {
+
+               /* Error */
+               errno = -EBADFD;
+
+       }
+
+       (*ppos) += bytes_written;
+
+       up(&sisusb->lock);
+
+       return errno ? errno : bytes_written;
+}
+
+static loff_t
+sisusb_lseek(struct file *file, loff_t offset, int orig)
+{
+       struct sisusb_usb_data *sisusb;
+       loff_t ret;
+
+       if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+               return -ENODEV;
+
+       down(&sisusb->lock);
+
+       /* Sanity check */
+       if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
+               up(&sisusb->lock);
+               return -ENODEV;
+       }
+
+       switch (orig) {
+               case 0:
+                       file->f_pos = offset;
+                       ret = file->f_pos;
+                       /* never negative, no force_successful_syscall needed */
+                       break;
+               case 1:
+                       file->f_pos += offset;
+                       ret = file->f_pos;
+                       /* never negative, no force_successful_syscall needed */
+                       break;
+               default:
+                       /* seeking relative to "end of file" is not supported */
+                       ret = -EINVAL;
+       }
+
+       up(&sisusb->lock);
+       return ret;
+}
+
+static int
+sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y,
+                                                       unsigned long arg)
+{
+       int     retval, port, length;
+       u32     address;
+
+       port = y->data3 -
+               SISUSB_PCI_PSEUDO_IOPORTBASE +
+               SISUSB_PCI_IOPORTBASE;
+
+       switch (y->operation) {
+               case SUCMD_GET:
+                       retval = sisusb_getidxreg(sisusb, port,
+                                                        y->data0, &y->data1);
+                       if (!retval) {
+                               if (copy_to_user((void __user *)arg, y,
+                                                       sizeof(*y)))
+                                       retval = -EFAULT;
+                       }
+                       break;
+
+               case SUCMD_SET:
+                       retval = sisusb_setidxreg(sisusb, port,
+                                               y->data0, y->data1);
+                       break;
+
+               case SUCMD_SETOR:
+                       retval = sisusb_setidxregor(sisusb, port,
+                                               y->data0, y->data1);
+                       break;
+
+               case SUCMD_SETAND:
+                       retval = sisusb_setidxregand(sisusb, port,
+                                               y->data0, y->data1);
+                       break;
+
+               case SUCMD_SETANDOR:
+                       retval = sisusb_setidxregandor(sisusb, port,
+                                               y->data0, y->data1, y->data2);
+                       break;
+
+               case SUCMD_SETMASK:
+                       retval = sisusb_setidxregmask(sisusb, port,
+                                               y->data0, y->data1, y->data2);
+                       break;
+
+               case SUCMD_CLRSCR:
+                       length = (y->data0 << 16) | (y->data1 << 8) | y->data2;
+                       address = y->data3 -
+                               SISUSB_PCI_PSEUDO_MEMBASE +
+                               SISUSB_PCI_MEMBASE;
+                       retval = sisusb_clear_vram(sisusb, address, length);
+                       break;
+
+               default:
+                       retval = -EINVAL;
+       }
+
+       if(retval > 0)
+               retval = -EIO;
+
+       return retval;
+}
+
+static int
+sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+                                                       unsigned long arg)
+{
+       struct sisusb_usb_data *sisusb;
+       struct sisusb_info x;
+       struct sisusb_command y;
+       int     retval = 0;
+       u32 __user *argp = (u32 __user *)arg;
+
+       if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
+               return -ENODEV;
+
+       down(&sisusb->lock);
+
+       /* Sanity check */
+       if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) {
+               retval = -ENODEV;
+               goto err_out;
+       }
+
+       switch (cmd) {
+
+               case SISUSB_GET_CONFIG_SIZE:
+
+                       if (put_user(sizeof(x), argp))
+                               retval = -EFAULT;
+
+                       break;
+
+               case SISUSB_GET_CONFIG:
+
+                       x.sisusb_id         = SISUSB_ID;
+                       x.sisusb_version    = SISUSB_VERSION;
+                       x.sisusb_revision   = SISUSB_REVISION;
+                       x.sisusb_patchlevel = SISUSB_PATCHLEVEL;
+                       x.sisusb_gfxinit    = sisusb->gfxinit;
+                       x.sisusb_vrambase   = SISUSB_PCI_PSEUDO_MEMBASE;
+                       x.sisusb_mmiobase   = SISUSB_PCI_PSEUDO_MMIOBASE;
+                       x.sisusb_iobase     = SISUSB_PCI_PSEUDO_IOPORTBASE;
+                       x.sisusb_pcibase    = SISUSB_PCI_PSEUDO_PCIBASE;
+                       x.sisusb_vramsize   = sisusb->vramsize;
+                       x.sisusb_minor      = sisusb->minor;
+                       x.sisusb_fbdevactive= 0;
+
+                       if (copy_to_user((void __user *)arg, &x, sizeof(x)))
+                               retval = -EFAULT;
+
+                       break;
+
+               case SISUSB_COMMAND:
+
+                       if (copy_from_user(&y, (void __user *)arg, sizeof(y)))
+                               retval = -EFAULT;
+                       else
+                               retval = sisusb_handle_command(sisusb, &y, arg);
+
+                       break;
+
+               default:
+                       retval = -EINVAL;
+                       break;
+       }
+
+err_out:
+       up(&sisusb->lock);
+       return retval;
+}
+
+#ifdef SISUSB_NEW_CONFIG_COMPAT
+static long
+sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+       long retval;
+
+       switch (cmd) {
+               case SISUSB_GET_CONFIG_SIZE:
+               case SISUSB_GET_CONFIG:
+               case SISUSB_COMMAND:
+                       lock_kernel();
+                       retval = sisusb_ioctl(f->f_dentry->d_inode, f, cmd, arg);
+                       unlock_kernel();
+                       return retval;
+
+               default:
+                       return -ENOIOCTLCMD;
+       }
+}
+#endif
+
+static struct file_operations usb_sisusb_fops = {
+       .owner =        THIS_MODULE,
+       .open =         sisusb_open,
+       .release =      sisusb_release,
+       .read =         sisusb_read,
+       .write =        sisusb_write,
+       .llseek =       sisusb_lseek,
+#ifdef SISUSB_NEW_CONFIG_COMPAT
+       .compat_ioctl = sisusb_compat_ioctl,
+#endif
+       .ioctl =        sisusb_ioctl
+};
+
+static struct usb_class_driver usb_sisusb_class = {
+       .name =         "usb/sisusbvga%d",
+       .fops =         &usb_sisusb_fops,
+       .mode =         S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
+       .minor_base =   SISUSB_MINOR
+};
+
+static int sisusb_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       struct usb_device *dev = interface_to_usbdev(intf);
+       struct sisusb_usb_data *sisusb;
+       int retval = 0, i;
+       const char *memfail =
+               KERN_ERR
+               "sisusbvga[%d]: Failed to allocate memory for %s buffer\n";
+
+       printk(KERN_INFO "sisusb: USB2VGA dongle found at address %d\n",
+               dev->devnum);
+
+       /* Allocate memory for our private */
+       if (!(sisusb = kmalloc(sizeof(*sisusb), GFP_KERNEL))) {
+               printk(KERN_ERR
+                       "sisusb: Failed to allocate memory for private data\n");
+               return -ENOMEM;
+       }
+       memset(sisusb, 0, sizeof(*sisusb));
+       kref_init(&sisusb->kref);
+
+       init_MUTEX(&(sisusb->lock));
+
+       /* Register device */
+       if ((retval = usb_register_dev(intf, &usb_sisusb_class))) {
+               printk(KERN_ERR
+                       "sisusb: Failed to get a minor for device %d\n",
+                       dev->devnum);
+               retval = -ENODEV;
+               goto error_1;
+       }
+
+       sisusb->sisusb_dev = dev;
+       sisusb->minor      = intf->minor;
+       sisusb->vrambase   = SISUSB_PCI_MEMBASE;
+       sisusb->mmiobase   = SISUSB_PCI_MMIOBASE;
+       sisusb->mmiosize   = SISUSB_PCI_MMIOSIZE;
+       sisusb->ioportbase = SISUSB_PCI_IOPORTBASE;
+       /* Everything else is zero */
+
+       /* Allocate buffers */
+       sisusb->ibufsize = SISUSB_IBUF_SIZE;
+       if (!(sisusb->ibuf = usb_buffer_alloc(dev, SISUSB_IBUF_SIZE,
+                                       GFP_KERNEL, &sisusb->transfer_dma_in))) {
+               printk(memfail, "input", sisusb->minor);
+               retval = -ENOMEM;
+               goto error_2;
+       }
+
+       sisusb->numobufs = 0;
+       sisusb->obufsize = SISUSB_OBUF_SIZE;
+       for (i = 0; i < NUMOBUFS; i++) {
+               if (!(sisusb->obuf[i] = usb_buffer_alloc(dev, SISUSB_OBUF_SIZE,
+                                       GFP_KERNEL,
+                                       &sisusb->transfer_dma_out[i]))) {
+                       if (i == 0) {
+                               printk(memfail, "output", sisusb->minor);
+                               retval = -ENOMEM;
+                               goto error_3;
+                       }
+                       break;
+               } else
+                       sisusb->numobufs++;
+
+       }
+
+       /* Allocate URBs */
+       if (!(sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL))) {
+               printk(KERN_ERR
+                       "sisusbvga[%d]: Failed to allocate URBs\n",
+                       sisusb->minor);
+               retval = -ENOMEM;
+               goto error_3;
+       }
+       sisusb->completein = 1;
+
+       for (i = 0; i < sisusb->numobufs; i++) {
+               if (!(sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL))) {
+                       printk(KERN_ERR
+                               "sisusbvga[%d]: Failed to allocate URBs\n",
+                               sisusb->minor);
+                       retval = -ENOMEM;
+                       goto error_4;
+               }
+               sisusb->urbout_context[i].sisusb = (void *)sisusb;
+               sisusb->urbout_context[i].urbindex = i;
+               sisusb->urbstatus[i] = 0;
+       }
+
+       printk(KERN_INFO "sisusbvga[%d]: Allocated %d output buffers\n",
+                                       sisusb->minor, sisusb->numobufs);
+
+       /* Do remaining init stuff */
+
+       init_waitqueue_head(&sisusb->wait_q);
+
+       usb_set_intfdata(intf, sisusb);
+
+#ifdef SISUSB_OLD_CONFIG_COMPAT
+       {
+       int ret;
+       /* Our ioctls are all "32/64bit compatible" */
+       ret =  register_ioctl32_conversion(SISUSB_GET_CONFIG_SIZE, NULL);
+       ret |= register_ioctl32_conversion(SISUSB_GET_CONFIG,      NULL);
+       ret |= register_ioctl32_conversion(SISUSB_COMMAND,         NULL);
+       if (ret)
+               printk(KERN_ERR
+                       "sisusbvga[%d]: Error registering ioctl32 "
+                       "translations\n",
+                       sisusb->minor);
+       else
+               sisusb->ioctl32registered = 1;
+
+       }
+#endif
+
+       sisusb->present = 1;
+
+       if (dev->speed == USB_SPEED_HIGH) {
+               if (sisusb_init_gfxdevice(sisusb, 1))
+                       printk(KERN_ERR
+                               "sisusbvga[%d]: Failed to early "
+                               "initialize device\n",
+                               sisusb->minor);
+
+       } else
+               printk(KERN_INFO
+                       "sisusbvga[%d]: Not attached to USB 2.0 hub, "
+                       "deferring init\n",
+                       sisusb->minor);
+
+       sisusb->ready = 1;
+
+       return 0;
+
+error_4:
+       sisusb_free_urbs(sisusb);
+error_3:
+       sisusb_free_buffers(sisusb);
+error_2:
+       usb_deregister_dev(intf, &usb_sisusb_class);
+error_1:
+       kfree(sisusb);
+       return retval;
+}
+
+static void sisusb_disconnect(struct usb_interface *intf)
+{
+       struct sisusb_usb_data *sisusb;
+       int minor;
+
+       down(&disconnect_sem);
+
+       /* This should *not* happen */
+       if (!(sisusb = usb_get_intfdata(intf))) {
+               up(&disconnect_sem);
+               return;
+       }
+
+       down(&sisusb->lock);
+
+       /* Wait for all URBs to complete and kill them in case (MUST do) */
+       if (!sisusb_wait_all_out_complete(sisusb))
+               sisusb_kill_all_busy(sisusb);
+
+       minor = sisusb->minor;
+
+       usb_set_intfdata(intf, NULL);
+
+       usb_deregister_dev(intf, &usb_sisusb_class);
+
+#ifdef SISUSB_OLD_CONFIG_COMPAT
+       if (sisusb->ioctl32registered) {
+               int ret;
+               sisusb->ioctl32registered = 0;
+               ret =  unregister_ioctl32_conversion(SISUSB_GET_CONFIG_SIZE);
+               ret |= unregister_ioctl32_conversion(SISUSB_GET_CONFIG);
+               ret |= unregister_ioctl32_conversion(SISUSB_COMMAND);
+               if (ret) {
+                       printk(KERN_ERR
+                               "sisusbvga[%d]: Error unregistering "
+                               "ioctl32 translations\n",
+                               minor);
+               }
+       }
+#endif
+
+       sisusb->present = 0;
+       sisusb->ready = 0;
+
+       up(&sisusb->lock);
+
+       /* decrement our usage count */
+       kref_put(&sisusb->kref, sisusb_delete);
+
+       up(&disconnect_sem);
+
+       printk(KERN_INFO "sisusbvga[%d]: Disconnected\n", minor);
+}
+
+static struct usb_device_id sisusb_table [] = {
+       { USB_DEVICE(0x0711, 0x0900) },
+       { USB_DEVICE(0x182d, 0x021c) },
+       { USB_DEVICE(0x182d, 0x0269) },
+       { }
+};
+
+MODULE_DEVICE_TABLE (usb, sisusb_table);
+
+static struct usb_driver sisusb_driver = {
+       .owner =        THIS_MODULE,
+       .name =         "sisusb",
+       .probe =        sisusb_probe,
+       .disconnect =   sisusb_disconnect,
+       .id_table =     sisusb_table,
+};
+
+static int __init usb_sisusb_init(void)
+{
+       int retval;
+
+       if (!(retval = usb_register(&sisusb_driver))) {
+               printk(KERN_INFO "sisusb: Driver version %d.%d.%d\n",
+                       SISUSB_VERSION, SISUSB_REVISION, SISUSB_PATCHLEVEL);
+               printk(KERN_INFO
+                       "sisusb: Copyright (C) 2005 Thomas Winischhofer\n");
+       }
+
+       return retval;
+}
+
+static void __exit usb_sisusb_exit(void)
+{
+       usb_deregister(&sisusb_driver);
+}
+
+module_init(usb_sisusb_init);
+module_exit(usb_sisusb_exit);
+
+MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>");
+MODULE_DESCRIPTION("sisusb - Driver for Net2280/SiS315-based USB2VGA dongles");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/usb/misc/sisusbvga/sisusb.h b/drivers/usb/misc/sisusbvga/sisusb.h
new file mode 100644 (file)
index 0000000..1306d00
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * sisusb - usb kernel driver for Net2280/SiS315 based USB2VGA dongles
+ *
+ * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria
+ *
+ * If distributed as part of the Linux kernel, this code is licensed under the
+ * terms of the GPL v2.
+ *
+ * Otherwise, the following license terms apply:
+ *
+ * * 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) The name of the author may not be used to endorse or promote products
+ * *    derived from this software without specific prior written permission.
+ * *
+ * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED 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.
+ *
+ * Author:     Thomas Winischhofer <thomas@winischhofer.net>
+ *
+ */
+
+#ifndef _SISUSB_H_
+#define _SISUSB_H_
+
+#ifdef CONFIG_COMPAT
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,10)
+#include <linux/ioctl32.h>
+#define SISUSB_OLD_CONFIG_COMPAT
+#else
+#define SISUSB_NEW_CONFIG_COMPAT
+#endif
+#endif
+
+/* Version Information */
+
+#define SISUSB_VERSION         0
+#define SISUSB_REVISION        0
+#define SISUSB_PATCHLEVEL      7
+
+/* USB related */
+
+#define SISUSB_MINOR   133             /* FIXME */
+
+/* Size of the sisusb input/output buffers */
+#define SISUSB_IBUF_SIZE  0x01000
+#define SISUSB_OBUF_SIZE  0x10000      /* fixed */
+
+#define NUMOBUFS 8                     /* max number of output buffers/output URBs */
+
+/* About endianness:
+ *
+ * 1) I/O ports, PCI config registers. The read/write()
+ *    calls emulate inX/outX. Hence, the data is
+ *    expected/delivered in machine endiannes by this
+ *    driver.
+ * 2) Video memory. The data is copied 1:1. There is
+ *    no swapping. Ever. This means for userland that
+ *    the data has to be prepared properly. (Hint:
+ *    think graphics data format, command queue,
+ *    hardware cursor.)
+ * 3) MMIO. Data is copied 1:1. MMIO must be swapped
+ *    properly by userland.
+ *
+ */
+
+#ifdef __BIG_ENDIAN
+#define SISUSB_CORRECT_ENDIANNESS_PACKET(p)            \
+       do {                                            \
+               p->header  = cpu_to_le16(p->header);    \
+               p->address = cpu_to_le32(p->address);   \
+               p->data    = cpu_to_le32(p->data);      \
+       } while(0)
+#else
+#define SISUSB_CORRECT_ENDIANNESS_PACKET(p)
+#endif
+
+struct sisusb_usb_data;
+
+struct sisusb_urb_context {            /* urb->context for outbound bulk URBs */
+       struct sisusb_usb_data *sisusb;
+       int urbindex;
+       int *actual_length;
+};
+
+struct sisusb_usb_data {
+       struct usb_device *sisusb_dev;
+       struct usb_interface *interface;
+       struct kref kref;
+       wait_queue_head_t wait_q;       /* for syncind and timeouts */
+       struct semaphore lock;          /* general race avoidance */
+       unsigned int ifnum;             /* interface number of the USB device */
+       int minor;                      /* minor (for logging clarity) */
+       int isopen;                     /* !=0 if open */
+       int present;                    /* !=0 if device is present on the bus */
+       int ready;                      /* !=0 if device is ready for userland */
+#ifdef SISUSB_OLD_CONFIG_COMPAT
+       int ioctl32registered;
+#endif
+       int numobufs;                   /* number of obufs = number of out urbs */
+       char *obuf[NUMOBUFS], *ibuf;    /* transfer buffers */
+       int obufsize, ibufsize;
+       dma_addr_t transfer_dma_out[NUMOBUFS];
+       dma_addr_t transfer_dma_in;
+       struct urb *sisurbout[NUMOBUFS];
+       struct urb *sisurbin;
+       unsigned char urbstatus[NUMOBUFS];
+       unsigned char completein;
+       struct sisusb_urb_context urbout_context[NUMOBUFS];
+       unsigned long flagb0;
+       unsigned long vrambase;         /* framebuffer base */
+       unsigned int vramsize;          /* framebuffer size (bytes) */
+       unsigned long mmiobase;
+       unsigned int mmiosize;
+       unsigned long ioportbase;
+       unsigned char devinit;          /* device initialized? */
+       unsigned char gfxinit;          /* graphics core initialized? */
+       unsigned short chipid, chipvendor;
+       unsigned short chiprevision;
+};
+
+#define to_sisusb_dev(d) container_of(d, struct sisusb_usb_data, kref)
+
+/* USB transport related */
+
+/* urbstatus */
+#define SU_URB_BUSY   1
+#define SU_URB_ALLOC  2
+
+/* Endpoints */
+
+#define SISUSB_EP_GFX_IN       0x0e    /* gfx std packet out(0e)/in(8e) */
+#define SISUSB_EP_GFX_OUT      0x0e
+
+#define SISUSB_EP_GFX_BULK_OUT 0x01    /* gfx mem bulk out/in */
+#define SISUSB_EP_GFX_BULK_IN  0x02    /* ? 2 is "OUT" ? */
+
+#define SISUSB_EP_GFX_LBULK_OUT        0x03    /* gfx large mem bulk out */
+
+#define SISUSB_EP_UNKNOWN_04   0x04    /* ? 4 is "OUT" ? - unused */
+
+#define SISUSB_EP_BRIDGE_IN    0x0d    /* Net2280 out(0d)/in(8d) */
+#define SISUSB_EP_BRIDGE_OUT   0x0d
+
+#define SISUSB_TYPE_MEM                0
+#define SISUSB_TYPE_IO         1
+
+struct sisusb_packet {
+       unsigned short header;
+       u32 address;
+       u32 data;
+} __attribute__((__packed__));
+
+#define CLEARPACKET(packet) memset(packet, 0, 10)
+
+/* PCI bridge related */
+
+#define SISUSB_PCI_MEMBASE     0xd0000000
+#define SISUSB_PCI_MMIOBASE    0xe4000000
+#define SISUSB_PCI_IOPORTBASE  0x0000d000
+
+#define SISUSB_PCI_PSEUDO_MEMBASE      0x10000000
+#define SISUSB_PCI_PSEUDO_MMIOBASE     0x20000000
+#define SISUSB_PCI_PSEUDO_IOPORTBASE   0x0000d000
+#define SISUSB_PCI_PSEUDO_PCIBASE      0x00010000
+
+#define SISUSB_PCI_MMIOSIZE    (128*1024)
+#define SISUSB_PCI_PCONFSIZE   0x5c
+
+/* graphics core related */
+
+#define AROFFSET       0x40
+#define ARROFFSET      0x41
+#define GROFFSET       0x4e
+#define SROFFSET       0x44
+#define CROFFSET       0x54
+#define MISCROFFSET    0x4c
+#define MISCWOFFSET    0x42
+#define INPUTSTATOFFSET 0x5A
+#define PART1OFFSET    0x04
+#define PART2OFFSET    0x10
+#define PART3OFFSET    0x12
+#define PART4OFFSET    0x14
+#define PART5OFFSET    0x16
+#define CAPTUREOFFSET  0x00
+#define VIDEOOFFSET    0x02
+#define COLREGOFFSET   0x48
+#define PELMASKOFFSET  0x46
+#define VGAENABLE      0x43
+
+#define SISAR          SISUSB_PCI_IOPORTBASE + AROFFSET
+#define SISARR         SISUSB_PCI_IOPORTBASE + ARROFFSET
+#define SISGR          SISUSB_PCI_IOPORTBASE + GROFFSET
+#define SISSR          SISUSB_PCI_IOPORTBASE + SROFFSET
+#define SISCR          SISUSB_PCI_IOPORTBASE + CROFFSET
+#define SISMISCR       SISUSB_PCI_IOPORTBASE + MISCROFFSET
+#define SISMISCW       SISUSB_PCI_IOPORTBASE + MISCWOFFSET
+#define SISINPSTAT     SISUSB_PCI_IOPORTBASE + INPUTSTATOFFSET
+#define SISPART1       SISUSB_PCI_IOPORTBASE + PART1OFFSET
+#define SISPART2       SISUSB_PCI_IOPORTBASE + PART2OFFSET
+#define SISPART3       SISUSB_PCI_IOPORTBASE + PART3OFFSET
+#define SISPART4       SISUSB_PCI_IOPORTBASE + PART4OFFSET
+#define SISPART5       SISUSB_PCI_IOPORTBASE + PART5OFFSET
+#define SISCAP         SISUSB_PCI_IOPORTBASE + CAPTUREOFFSET
+#define SISVID         SISUSB_PCI_IOPORTBASE + VIDEOOFFSET
+#define SISCOLIDXR     SISUSB_PCI_IOPORTBASE + COLREGOFFSET - 1
+#define SISCOLIDX      SISUSB_PCI_IOPORTBASE + COLREGOFFSET
+#define SISCOLDATA     SISUSB_PCI_IOPORTBASE + COLREGOFFSET + 1
+#define SISCOL2IDX     SISPART5
+#define SISCOL2DATA    SISPART5 + 1
+#define SISPEL         SISUSB_PCI_IOPORTBASE + PELMASKOFFSET
+#define SISVGAEN       SISUSB_PCI_IOPORTBASE + VGAENABLE
+#define SISDACA                SISCOLIDX
+#define SISDACD                SISCOLDATA
+
+/* ioctl related */
+
+/* Structure argument for SISUSB_GET_INFO ioctl  */
+struct sisusb_info {
+       __u32   sisusb_id;              /* for identifying sisusb */
+#define SISUSB_ID  0x53495355          /* Identify myself with 'SISU' */
+       __u8    sisusb_version;
+       __u8    sisusb_revision;
+       __u8    sisusb_patchlevel;
+       __u8    sisusb_gfxinit;         /* graphics core initialized? */
+
+       __u32   sisusb_vrambase;
+       __u32   sisusb_mmiobase;
+       __u32   sisusb_iobase;
+       __u32   sisusb_pcibase;
+
+       __u32   sisusb_vramsize;        /* framebuffer size in bytes */
+
+       __u32   sisusb_minor;
+
+       __u32   sisusb_fbdevactive;     /* != 0 if framebuffer device active */
+
+       __u8    sisusb_reserved[32];    /* for future use */
+};
+
+struct sisusb_command {
+       __u8   operation;       /* see below */
+       __u8   data0;           /* operation dependent */
+       __u8   data1;           /* operation dependent */
+       __u8   data2;           /* operation dependent */
+       __u32  data3;           /* operation dependent */
+       __u32  data4;           /* for future use */
+};
+
+#define SUCMD_GET      0x01    /* for all: data0 = index, data3 = port */
+#define SUCMD_SET      0x02    /* data1 = value */
+#define SUCMD_SETOR    0x03    /* data1 = or */
+#define SUCMD_SETAND   0x04    /* data1 = and */
+#define SUCMD_SETANDOR 0x05    /* data1 = and, data2 = or */
+#define SUCMD_SETMASK  0x06    /* data1 = data, data2 = mask */
+
+#define SUCMD_CLRSCR   0x07    /* data0:1:2 = length, data3 = address */
+
+#define SISUSB_COMMAND         _IOWR(0xF3,0x3D,struct sisusb_command)
+#define SISUSB_GET_CONFIG_SIZE         _IOR(0xF3,0x3E,__u32)
+#define SISUSB_GET_CONFIG      _IOR(0xF3,0x3F,struct sisusb_info)
+
+#endif /* SISUSB_H */
+
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
new file mode 100644 (file)
index 0000000..aa9d008
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * mon_main.c: Main file, module initiation and exit, registrations, etc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/debugfs.h>
+#include <linux/smp_lock.h>
+
+#include "usb_mon.h"
+#include "../core/hcd.h"
+
+static void mon_submit(struct usb_bus *ubus, struct urb *urb);
+static void mon_complete(struct usb_bus *ubus, struct urb *urb);
+static void mon_stop(struct mon_bus *mbus);
+static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus);
+static void mon_bus_drop(struct kref *r);
+static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus);
+
+DECLARE_MUTEX(mon_lock);
+
+static struct dentry *mon_dir;         /* /dbg/usbmon */
+static LIST_HEAD(mon_buses);           /* All buses we know: struct mon_bus */
+
+/*
+ * Link a reader into the bus.
+ *
+ * This must be called with mon_lock taken because of mbus->ref.
+ */
+void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r)
+{
+       unsigned long flags;
+       struct usb_bus *ubus;
+
+       spin_lock_irqsave(&mbus->lock, flags);
+       if (mbus->nreaders == 0) {
+               ubus = mbus->u_bus;
+               if (ubus->monitored) {
+                       /*
+                        * Something is really broken, refuse to go on and
+                        * possibly corrupt ops pointers or worse.
+                        */
+                       printk(KERN_ERR TAG ": bus %d is already monitored\n",
+                           ubus->busnum);
+                       spin_unlock_irqrestore(&mbus->lock, flags);
+                       return;
+               }
+               ubus->monitored = 1;
+       }
+       mbus->nreaders++;
+       list_add_tail(&r->r_link, &mbus->r_list);
+       spin_unlock_irqrestore(&mbus->lock, flags);
+
+       kref_get(&mbus->ref);
+}
+
+/*
+ * Unlink reader from the bus.
+ *
+ * This is called with mon_lock taken, so we can decrement mbus->ref.
+ */
+void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&mbus->lock, flags);
+       list_del(&r->r_link);
+       --mbus->nreaders;
+       if (mbus->nreaders == 0)
+               mon_stop(mbus);
+       spin_unlock_irqrestore(&mbus->lock, flags);
+
+       kref_put(&mbus->ref, mon_bus_drop);
+}
+
+/*
+ */
+static void mon_submit(struct usb_bus *ubus, struct urb *urb)
+{
+       struct mon_bus *mbus;
+       unsigned long flags;
+       struct list_head *pos;
+       struct mon_reader *r;
+
+       mbus = ubus->mon_bus;
+       if (mbus == NULL)
+               goto out_unlocked;
+
+       spin_lock_irqsave(&mbus->lock, flags);
+       if (mbus->nreaders == 0)
+               goto out_locked;
+
+       list_for_each (pos, &mbus->r_list) {
+               r = list_entry(pos, struct mon_reader, r_link);
+               r->rnf_submit(r->r_data, urb);
+       }
+
+       spin_unlock_irqrestore(&mbus->lock, flags);
+       return;
+
+out_locked:
+       spin_unlock_irqrestore(&mbus->lock, flags);
+out_unlocked:
+       return;
+}
+
+/*
+ */
+static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int err)
+{
+       struct mon_bus *mbus;
+
+       mbus = ubus->mon_bus;
+       if (mbus == NULL)
+               goto out_unlocked;
+
+       /*
+        * XXX Capture the error code and the 'E' event.
+        */
+
+       return;
+
+out_unlocked:
+       return;
+}
+
+/*
+ */
+static void mon_complete(struct usb_bus *ubus, struct urb *urb)
+{
+       struct mon_bus *mbus;
+       unsigned long flags;
+       struct list_head *pos;
+       struct mon_reader *r;
+
+       mbus = ubus->mon_bus;
+       if (mbus == NULL) {
+               /*
+                * This should not happen.
+                * At this point we do not even know the bus number...
+                */
+               printk(KERN_ERR TAG ": Null mon bus in URB, pipe 0x%x\n",
+                   urb->pipe);
+               return;
+       }
+
+       spin_lock_irqsave(&mbus->lock, flags);
+       list_for_each (pos, &mbus->r_list) {
+               r = list_entry(pos, struct mon_reader, r_link);
+               r->rnf_complete(r->r_data, urb);
+       }
+       spin_unlock_irqrestore(&mbus->lock, flags);
+}
+
+/* int (*unlink_urb) (struct urb *urb, int status); */
+
+/*
+ * Stop monitoring.
+ * Obviously this must be well locked, so no need to play with mb's.
+ */
+static void mon_stop(struct mon_bus *mbus)
+{
+       struct usb_bus *ubus = mbus->u_bus;
+
+       /*
+        * A stop can be called for a dissolved mon_bus in case of
+        * a reader staying across an rmmod foo_hcd.
+        */
+       if (ubus != NULL) {
+               ubus->monitored = 0;
+               mb();
+       }
+}
+
+/*
+ * Add a USB bus (usually by a modprobe foo-hcd)
+ *
+ * This does not return an error code because the core cannot care less
+ * if monitoring is not established.
+ */
+static void mon_bus_add(struct usb_bus *ubus)
+{
+       mon_bus_init(mon_dir, ubus);
+}
+
+/*
+ * Remove a USB bus (either from rmmod foo-hcd or from a hot-remove event).
+ */
+static void mon_bus_remove(struct usb_bus *ubus)
+{
+       struct mon_bus *mbus = ubus->mon_bus;
+
+       down(&mon_lock);
+       list_del(&mbus->bus_link);
+       debugfs_remove(mbus->dent_t);
+       debugfs_remove(mbus->dent_s);
+
+       mon_dissolve(mbus, ubus);
+       kref_put(&mbus->ref, mon_bus_drop);
+       up(&mon_lock);
+}
+
+/*
+ * Ops
+ */
+static struct usb_mon_operations mon_ops_0 = {
+       .urb_submit =   mon_submit,
+       .urb_submit_error = mon_submit_error,
+       .urb_complete = mon_complete,
+       .bus_add =      mon_bus_add,
+       .bus_remove =   mon_bus_remove,
+};
+
+/*
+ * Tear usb_bus and mon_bus apart.
+ */
+static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus)
+{
+
+       /*
+        * Never happens, but...
+        */
+       if (ubus->monitored) {
+               printk(KERN_ERR TAG ": bus %d is dissolved while monitored\n",
+                   ubus->busnum);
+               ubus->monitored = 0;
+               mb();
+       }
+
+       ubus->mon_bus = NULL;
+       mbus->u_bus = NULL;
+       mb();
+       // usb_bus_put(ubus);
+}
+
+/*
+ */
+static void mon_bus_drop(struct kref *r)
+{
+       struct mon_bus *mbus = container_of(r, struct mon_bus, ref);
+       kfree(mbus);
+}
+
+/*
+ * Initialize a bus for us:
+ *  - allocate mon_bus
+ *  - refcount USB bus struct
+ *  - link
+ */
+static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
+{
+       struct dentry *d;
+       struct mon_bus *mbus;
+       enum { NAMESZ = 10 };
+       char name[NAMESZ];
+       int rc;
+
+       if ((mbus = kmalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL)
+               goto err_alloc;
+       memset(mbus, 0, sizeof(struct mon_bus));
+       kref_init(&mbus->ref);
+       spin_lock_init(&mbus->lock);
+       INIT_LIST_HEAD(&mbus->r_list);
+
+       /*
+        * This usb_bus_get here is superfluous, because we receive
+        * a notification if usb_bus is about to be removed.
+        */
+       // usb_bus_get(ubus);
+       mbus->u_bus = ubus;
+       ubus->mon_bus = mbus;
+
+       rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
+       if (rc <= 0 || rc >= NAMESZ)
+               goto err_print_t;
+       d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_text);
+       if (d == NULL)
+               goto err_create_t;
+       mbus->dent_t = d;
+
+       rc = snprintf(name, NAMESZ, "%ds", ubus->busnum);
+       if (rc <= 0 || rc >= NAMESZ)
+               goto err_print_s;
+       d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_stat);
+       if (d == NULL)
+               goto err_create_s;
+       mbus->dent_s = d;
+
+       down(&mon_lock);
+       list_add_tail(&mbus->bus_link, &mon_buses);
+       up(&mon_lock);
+       return;
+
+err_create_s:
+err_print_s:
+       debugfs_remove(mbus->dent_t);
+err_create_t:
+err_print_t:
+       kfree(mbus);
+err_alloc:
+       return;
+}
+
+static int __init mon_init(void)
+{
+       struct usb_bus *ubus;
+       struct dentry *mondir;
+
+       mondir = debugfs_create_dir("usbmon", NULL);
+       if (IS_ERR(mondir)) {
+               printk(KERN_NOTICE TAG ": debugs is not available\n");
+               return -ENODEV;
+       }
+       if (mondir == NULL) {
+               printk(KERN_NOTICE TAG ": unable to create usbmon directory\n");
+               return -ENODEV;
+       }
+       mon_dir = mondir;
+
+       if (usb_mon_register(&mon_ops_0) != 0) {
+               printk(KERN_NOTICE TAG ": unable to register with the core\n");
+               debugfs_remove(mondir);
+               return -ENODEV;
+       }
+       // MOD_INC_USE_COUNT(which_module?);
+
+       down(&usb_bus_list_lock);
+       list_for_each_entry (ubus, &usb_bus_list, bus_list) {
+               mon_bus_init(mondir, ubus);
+       }
+       up(&usb_bus_list_lock);
+       return 0;
+}
+
+static void __exit mon_exit(void)
+{
+       struct mon_bus *mbus;
+       struct list_head *p;
+
+       usb_mon_deregister();
+
+       down(&mon_lock);
+       while (!list_empty(&mon_buses)) {
+               p = mon_buses.next;
+               mbus = list_entry(p, struct mon_bus, bus_link);
+               list_del(p);
+
+               debugfs_remove(mbus->dent_t);
+               debugfs_remove(mbus->dent_s);
+
+               /*
+                * This never happens, because the open/close paths in
+                * file level maintain module use counters and so rmmod fails
+                * before reaching here. However, better be safe...
+                */
+               if (mbus->nreaders) {
+                       printk(KERN_ERR TAG
+                           ": Outstanding opens (%d) on usb%d, leaking...\n",
+                           mbus->nreaders, mbus->u_bus->busnum);
+                       atomic_set(&mbus->ref.refcount, 2);     /* Force leak */
+               }
+
+               mon_dissolve(mbus, mbus->u_bus);
+               kref_put(&mbus->ref, mon_bus_drop);
+       }
+       up(&mon_lock);
+
+       debugfs_remove(mon_dir);
+}
+
+module_init(mon_init);
+module_exit(mon_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c
new file mode 100644 (file)
index 0000000..6e4b165
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * This is the 's' or 'stat' reader which debugs usbmon itself.
+ * Note that this code blows through locks, so make sure that
+ * /dbg/usbmon/0s is well protected from non-root users.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+
+#include "usb_mon.h"
+
+#define STAT_BUF_SIZE  80
+
+struct snap {
+       int slen;
+       char str[STAT_BUF_SIZE];
+};
+
+static int mon_stat_open(struct inode *inode, struct file *file)
+{
+       struct mon_bus *mbus;
+       struct snap *sp;
+
+       if ((sp = kmalloc(sizeof(struct snap), GFP_KERNEL)) == NULL)
+               return -ENOMEM;
+
+       mbus = inode->u.generic_ip;
+
+       sp->slen = snprintf(sp->str, STAT_BUF_SIZE,
+           "nreaders %d text_lost %u\n",
+           mbus->nreaders, mbus->cnt_text_lost);
+
+       file->private_data = sp;
+       return 0;
+}
+
+static ssize_t mon_stat_read(struct file *file, char __user *buf,
+                               size_t nbytes, loff_t *ppos)
+{
+       struct snap *sp = file->private_data;
+       loff_t pos = *ppos;
+       int cnt;
+
+       if (pos < 0 || pos >= sp->slen)
+               return 0;
+       if (nbytes == 0)
+               return 0;
+       if ((cnt = sp->slen - pos) > nbytes)
+               cnt = nbytes;
+       if (copy_to_user(buf, sp->str + pos, cnt))
+               return -EFAULT;
+       *ppos = pos + cnt;
+       return cnt;
+}
+
+static int mon_stat_release(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+struct file_operations mon_fops_stat = {
+       .owner =        THIS_MODULE,
+       .open =         mon_stat_open,
+       .llseek =       no_llseek,
+       .read =         mon_stat_read,
+       /* .write =     mon_stat_write, */
+       /* .poll =              mon_stat_poll, */
+       /* .ioctl =     mon_stat_ioctl, */
+       .release =      mon_stat_release,
+};
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
new file mode 100644 (file)
index 0000000..755a457
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * This is a text format reader.
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/time.h>
+#include <asm/uaccess.h>
+
+#include "usb_mon.h"
+
+/*
+ * No, we do not want arbitrarily long data strings.
+ * Use the binary interface if you want to capture bulk data!
+ */
+#define DATA_MAX  32
+
+/*
+ * This limit exists to prevent OOMs when the user process stops reading.
+ */
+#define EVENT_MAX  25
+
+#define PRINTF_DFL  120
+
+struct mon_event_text {
+       struct list_head e_link;
+       int type;               /* submit, complete, etc. */
+       unsigned int pipe;      /* Pipe */
+       unsigned long id;       /* From pointer, most of the time */
+       unsigned int tstamp;
+       int length;             /* Depends on type: xfer length or act length */
+       int status;
+       char data_flag;
+       unsigned char data[DATA_MAX];
+};
+
+#define SLAB_NAME_SZ  30
+struct mon_reader_text {
+       kmem_cache_t *e_slab;
+       int nevents;
+       struct list_head e_list;
+       struct mon_reader r;    /* In C, parent class can be placed anywhere */
+
+       wait_queue_head_t wait;
+       int printf_size;
+       char *printf_buf;
+       struct semaphore printf_lock;
+
+       char slab_name[SLAB_NAME_SZ];
+};
+
+static void mon_text_ctor(void *, kmem_cache_t *, unsigned long);
+static void mon_text_dtor(void *, kmem_cache_t *, unsigned long);
+
+/*
+ * mon_text_submit
+ * mon_text_complete
+ *
+ * May be called from an interrupt.
+ *
+ * This is called with the whole mon_bus locked, so no additional lock.
+ */
+
+static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
+    int len, char ev_type)
+{
+       int pipe = urb->pipe;
+       unsigned char *data;
+
+       /*
+        * The check to see if it's safe to poke at data has an enormous
+        * number of corner cases, but it seems that the following is
+        * more or less safe.
+        *
+        * We do not even try to look transfer_buffer, because it can
+        * contain non-NULL garbage in case the upper level promised to
+        * set DMA for the HCD.
+        */
+       if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+               return 'D';
+
+       if (len <= 0)
+               return 'L';
+
+       if ((data = urb->transfer_buffer) == NULL)
+               return 'Z';     /* '0' would be not as pretty. */
+
+       /*
+        * Bulk is easy to shortcut reliably. 
+        * XXX Control needs setup packet taken.
+        * XXX Other pipe types need consideration. Currently, we overdo it
+        * and collect garbage for them: better more than less.
+        */
+       if (usb_pipebulk(pipe) || usb_pipecontrol(pipe)) {
+               if (usb_pipein(pipe)) {
+                       if (ev_type == 'S')
+                               return '<';
+               } else {
+                       if (ev_type == 'C')
+                               return '>';
+               }
+       }
+
+       if (len >= DATA_MAX)
+               len = DATA_MAX;
+       memcpy(ep->data, urb->transfer_buffer, len);
+       return 0;
+}
+
+static inline unsigned int mon_get_timestamp(void)
+{
+       struct timeval tval;
+       unsigned int stamp;
+
+       do_gettimeofday(&tval);
+       stamp = tval.tv_sec & 0xFFFF;   /* 2^32 = 4294967296. Limit to 4096s. */
+       stamp = stamp * 1000000 + tval.tv_usec;
+       return stamp;
+}
+
+static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
+    char ev_type)
+{
+       struct mon_event_text *ep;
+       unsigned int stamp;
+
+       stamp = mon_get_timestamp();
+
+       if (rp->nevents >= EVENT_MAX ||
+           (ep = kmem_cache_alloc(rp->e_slab, SLAB_ATOMIC)) == NULL) {
+               rp->r.m_bus->cnt_text_lost++;
+               return;
+       }
+
+       ep->type = ev_type;
+       ep->pipe = urb->pipe;
+       ep->id = (unsigned long) urb;
+       ep->tstamp = stamp;
+       ep->length = (ev_type == 'S') ?
+           urb->transfer_buffer_length : urb->actual_length;
+       /* Collecting status makes debugging sense for submits, too */
+       ep->status = urb->status;
+
+       ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type);
+
+       rp->nevents++;
+       list_add_tail(&ep->e_link, &rp->e_list);
+       wake_up(&rp->wait);
+}
+
+static void mon_text_submit(void *data, struct urb *urb)
+{
+       struct mon_reader_text *rp = data;
+       mon_text_event(rp, urb, 'S');
+}
+
+static void mon_text_complete(void *data, struct urb *urb)
+{
+       struct mon_reader_text *rp = data;
+       mon_text_event(rp, urb, 'C');
+}
+
+/*
+ * Fetch next event from the circular buffer.
+ */
+static struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp,
+    struct mon_bus *mbus)
+{
+       struct list_head *p;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mbus->lock, flags);
+       if (list_empty(&rp->e_list)) {
+               spin_unlock_irqrestore(&mbus->lock, flags);
+               return NULL;
+       }
+       p = rp->e_list.next;
+       list_del(p);
+       --rp->nevents;
+       spin_unlock_irqrestore(&mbus->lock, flags);
+       return list_entry(p, struct mon_event_text, e_link);
+}
+
+/*
+ */
+static int mon_text_open(struct inode *inode, struct file *file)
+{
+       struct mon_bus *mbus;
+       struct usb_bus *ubus;
+       struct mon_reader_text *rp;
+       int rc;
+
+       down(&mon_lock);
+       mbus = inode->u.generic_ip;
+       ubus = mbus->u_bus;
+
+       rp = kmalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
+       if (rp == NULL) {
+               rc = -ENOMEM;
+               goto err_alloc;
+       }
+       memset(rp, 0, sizeof(struct mon_reader_text));
+       INIT_LIST_HEAD(&rp->e_list);
+       init_waitqueue_head(&rp->wait);
+       init_MUTEX(&rp->printf_lock);
+
+       rp->printf_size = PRINTF_DFL;
+       rp->printf_buf = kmalloc(rp->printf_size, GFP_KERNEL);
+       if (rp->printf_buf == NULL) {
+               rc = -ENOMEM;
+               goto err_alloc_pr;
+       }
+
+       rp->r.m_bus = mbus;
+       rp->r.r_data = rp;
+       rp->r.rnf_submit = mon_text_submit;
+       rp->r.rnf_complete = mon_text_complete;
+
+       snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", ubus->busnum,
+           (long)rp);
+       rp->e_slab = kmem_cache_create(rp->slab_name,
+           sizeof(struct mon_event_text), sizeof(long), 0,
+           mon_text_ctor, mon_text_dtor);
+       if (rp->e_slab == NULL) {
+               rc = -ENOMEM;
+               goto err_slab;
+       }
+
+       mon_reader_add(mbus, &rp->r);
+
+       file->private_data = rp;
+       up(&mon_lock);
+       return 0;
+
+// err_busy:
+//     kmem_cache_destroy(rp->e_slab);
+err_slab:
+       kfree(rp->printf_buf);
+err_alloc_pr:
+       kfree(rp);
+err_alloc:
+       up(&mon_lock);
+       return rc;
+}
+
+/*
+ * For simplicity, we read one record in one system call and throw out
+ * what does not fit. This means that the following does not work:
+ *   dd if=/dbg/usbmon/0t bs=10
+ * Also, we do not allow seeks and do not bother advancing the offset.
+ */
+static ssize_t mon_text_read(struct file *file, char __user *buf,
+                               size_t nbytes, loff_t *ppos)
+{
+       struct mon_reader_text *rp = file->private_data;
+       struct mon_bus *mbus = rp->r.m_bus;
+       DECLARE_WAITQUEUE(waita, current);
+       struct mon_event_text *ep;
+       int cnt, limit;
+       char *pbuf;
+       char udir, utype;
+       int data_len, i;
+
+       add_wait_queue(&rp->wait, &waita);
+       set_current_state(TASK_INTERRUPTIBLE);
+       while ((ep = mon_text_fetch(rp, mbus)) == NULL) {
+               if (file->f_flags & O_NONBLOCK) {
+                       set_current_state(TASK_RUNNING);
+                       remove_wait_queue(&rp->wait, &waita);
+                       return -EWOULDBLOCK;    /* Same as EAGAIN in Linux */
+               }
+               /*
+                * We do not count nwaiters, because ->release is supposed
+                * to be called when all openers are gone only.
+                */
+               schedule();
+               if (signal_pending(current)) {
+                       remove_wait_queue(&rp->wait, &waita);
+                       return -EINTR;
+               }
+               set_current_state(TASK_INTERRUPTIBLE);
+       }
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&rp->wait, &waita);
+
+       down(&rp->printf_lock);
+       cnt = 0;
+       pbuf = rp->printf_buf;
+       limit = rp->printf_size;
+
+       udir = usb_pipein(ep->pipe) ? 'i' : 'o';
+       switch (usb_pipetype(ep->pipe)) {
+       case PIPE_ISOCHRONOUS:  utype = 'Z'; break;
+       case PIPE_INTERRUPT:    utype = 'I'; break;
+       case PIPE_CONTROL:      utype = 'C'; break;
+       default: /* PIPE_BULK */  utype = 'B';
+       }
+       cnt += snprintf(pbuf + cnt, limit - cnt,
+           "%lx %u %c %c%c:%03u:%02u %d %d",
+           ep->id, ep->tstamp, ep->type,
+           utype, udir, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe),
+           ep->status, ep->length);
+
+       if ((data_len = ep->length) > 0) {
+               if (ep->data_flag == 0) {
+                       cnt += snprintf(pbuf + cnt, limit - cnt, " =");
+                       if (data_len >= DATA_MAX)
+                               data_len = DATA_MAX;
+                       for (i = 0; i < data_len; i++) {
+                               if (i % 4 == 0) {
+                                       cnt += snprintf(pbuf + cnt, limit - cnt,
+                                           " ");
+                               }
+                               cnt += snprintf(pbuf + cnt, limit - cnt,
+                                   "%02x", ep->data[i]);
+                       }
+                       cnt += snprintf(pbuf + cnt, limit - cnt, "\n");
+               } else {
+                       cnt += snprintf(pbuf + cnt, limit - cnt,
+                           " %c\n", ep->data_flag);
+               }
+       } else {
+               cnt += snprintf(pbuf + cnt, limit - cnt, "\n");
+       }
+
+       if (copy_to_user(buf, rp->printf_buf, cnt))
+               cnt = -EFAULT;
+       up(&rp->printf_lock);
+       kmem_cache_free(rp->e_slab, ep);
+       return cnt;
+}
+
+static int mon_text_release(struct inode *inode, struct file *file)
+{
+       struct mon_reader_text *rp = file->private_data;
+       struct mon_bus *mbus;
+       /* unsigned long flags; */
+       struct list_head *p;
+       struct mon_event_text *ep;
+
+       down(&mon_lock);
+       mbus = inode->u.generic_ip;
+
+       if (mbus->nreaders <= 0) {
+               printk(KERN_ERR TAG ": consistency error on close\n");
+               up(&mon_lock);
+               return 0;
+       }
+       mon_reader_del(mbus, &rp->r);
+
+       /*
+        * In theory, e_list is protected by mbus->lock. However,
+        * after mon_reader_del has finished, the following is the case:
+        *  - we are not on reader list anymore, so new events won't be added;
+        *  - whole mbus may be dropped if it was orphaned.
+        * So, we better not touch mbus.
+        */
+       /* spin_lock_irqsave(&mbus->lock, flags); */
+       while (!list_empty(&rp->e_list)) {
+               p = rp->e_list.next;
+               ep = list_entry(p, struct mon_event_text, e_link);
+               list_del(p);
+               --rp->nevents;
+               kmem_cache_free(rp->e_slab, ep);
+       }
+       /* spin_unlock_irqrestore(&mbus->lock, flags); */
+
+       kmem_cache_destroy(rp->e_slab);
+       kfree(rp->printf_buf);
+       kfree(rp);
+
+       up(&mon_lock);
+       return 0;
+}
+
+struct file_operations mon_fops_text = {
+       .owner =        THIS_MODULE,
+       .open =         mon_text_open,
+       .llseek =       no_llseek,
+       .read =         mon_text_read,
+       /* .write =     mon_text_write, */
+       /* .poll =              mon_text_poll, */
+       /* .ioctl =     mon_text_ioctl, */
+       .release =      mon_text_release,
+};
+
+/*
+ * Slab interface: constructor.
+ */
+static void mon_text_ctor(void *mem, kmem_cache_t *slab, unsigned long sflags)
+{
+       /*
+        * Nothing to initialize. No, really!
+        * So, we fill it with garbage to emulate a reused object.
+        */
+       memset(mem, 0xe5, sizeof(struct mon_event_text));
+}
+
+static void mon_text_dtor(void *mem, kmem_cache_t *slab, unsigned long sflags)
+{
+       ;
+}
diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h
new file mode 100644 (file)
index 0000000..ed35c18
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ */
+
+#ifndef __USB_MON_H
+#define __USB_MON_H
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/kref.h>
+/* #include <linux/usb.h> */   /* We use struct pointers only in this header */
+
+#define TAG "usbmon"
+
+struct mon_bus {
+       struct list_head bus_link;
+       spinlock_t lock;
+       struct dentry *dent_s;          /* Debugging file */
+       struct dentry *dent_t;          /* Text interface file */
+       struct usb_bus *u_bus;
+
+       /* Ref */
+       int nreaders;                   /* Under mon_lock AND mbus->lock */
+       struct list_head r_list;        /* Chain of readers (usually one) */
+       struct kref ref;                /* Under mon_lock */
+
+       /* Stats */
+       unsigned int cnt_text_lost;
+};
+
+/*
+ * An instance of a process which opened a file (but can fork later)
+ */
+struct mon_reader {
+       struct list_head r_link;
+       struct mon_bus *m_bus;
+       void *r_data;           /* Use container_of instead? */
+
+       void (*rnf_submit)(void *data, struct urb *urb);
+       void (*rnf_complete)(void *data, struct urb *urb);
+};
+
+void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r);
+void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
+
+extern struct semaphore mon_lock;
+
+extern struct file_operations mon_fops_text;
+extern struct file_operations mon_fops_stat;
+
+#endif /* __USB_MON_H */
diff --git a/drivers/usb/net/zd1201.c b/drivers/usb/net/zd1201.c
new file mode 100644 (file)
index 0000000..341ae5f
--- /dev/null
@@ -0,0 +1,1906 @@
+/*
+ *     Driver for ZyDAS zd1201 based wireless USB devices.
+ *
+ *     Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org)
+ *
+ *     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.
+ *
+ *     Parts of this driver have been derived from a wlan-ng version
+ *     modified by ZyDAS. They also made documentation available, thanks!
+ *     Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <linux/string.h>
+#include <linux/if_arp.h>
+#include <linux/firmware.h>
+#include <ieee802_11.h>
+#include "zd1201.h"
+
+static struct usb_device_id zd1201_table[] = {
+       {USB_DEVICE(0x0586, 0x3400)}, /* Peabird Wireless USB Adapter */
+       {USB_DEVICE(0x0ace, 0x1201)}, /* ZyDAS ZD1201 Wireless USB Adapter */
+       {USB_DEVICE(0x050d, 0x6051)}, /* Belkin F5D6051 usb  adapter */
+       {USB_DEVICE(0x0db0, 0x6823)}, /* MSI UB11B usb  adapter */
+       {}
+};
+
+static int ap = 0;     /* Are we an AP or a normal station? */
+
+#define ZD1201_VERSION "0.15"
+
+MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>");
+MODULE_DESCRIPTION("Driver for ZyDAS ZD1201 based USB Wireless adapters");
+MODULE_VERSION(ZD1201_VERSION);
+MODULE_LICENSE("GPL");
+module_param(ap, int, 0);
+MODULE_PARM_DESC(ap, "If non-zero Access Point firmware will be loaded");
+MODULE_DEVICE_TABLE(usb, zd1201_table);
+
+
+static int zd1201_fw_upload(struct usb_device *dev, int apfw)
+{
+       const struct firmware *fw_entry;
+       char* data;
+       unsigned long len;
+       int err;
+       unsigned char ret;
+       char *buf;
+       char *fwfile;
+
+       if (apfw)
+               fwfile = "zd1201-ap.fw";
+       else
+               fwfile = "zd1201.fw";
+
+       err = request_firmware(&fw_entry, fwfile, &dev->dev);
+       if (err) {
+               dev_err(&dev->dev, "Failed to load %s firmware file!\n", fwfile);
+               dev_err(&dev->dev, "Make sure the hotplug firmware loader is installed.\n");
+               dev_err(&dev->dev, "Goto http://linux-lc100020.sourceforge.net for more info\n");
+               return err;
+       }
+
+       data = fw_entry->data;
+        len = fw_entry->size;
+
+       buf = kmalloc(1024, GFP_ATOMIC);
+       if (!buf)
+               goto exit;
+       
+       while (len > 0) {
+               int translen = (len > 1024) ? 1024 : len;
+               memcpy(buf, data, translen);
+
+               err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0,
+                   USB_DIR_OUT | 0x40, 0, 0, buf, translen,
+                   ZD1201_FW_TIMEOUT);
+               if (err < 0)
+                       goto exit;
+
+               len -= translen;
+               data += translen;
+       }
+                                        
+       err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x2,
+           USB_DIR_OUT | 0x40, 0, 0, NULL, 0, ZD1201_FW_TIMEOUT);
+       if (err < 0)
+               goto exit;
+                                                                                                                                                                
+       err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x4,
+           USB_DIR_IN | 0x40, 0,0, &ret, sizeof(ret), ZD1201_FW_TIMEOUT);
+       if (err < 0)
+               goto exit;
+                                                                                                                                                                                                                                                                                        
+       if (ret & 0x80) {
+               err = -EIO;
+               goto exit;
+       }
+
+       err = 0;
+exit:
+       kfree(buf);
+       release_firmware(fw_entry);
+       return err;
+}
+
+static void zd1201_usbfree(struct urb *urb, struct pt_regs *regs)
+{
+       struct zd1201 *zd = urb->context;
+
+       switch(urb->status) {
+               case -EILSEQ:
+               case -ENODEV:
+               case -ETIMEDOUT:
+               case -ENOENT:
+               case -EPIPE:
+               case -EOVERFLOW:
+               case -ESHUTDOWN:
+                       dev_warn(&zd->usb->dev, "%s: urb failed: %d\n", 
+                           zd->dev->name, urb->status);
+       }
+
+       kfree(urb->transfer_buffer);
+       usb_free_urb(urb);
+       return;
+}
+
+/* cmdreq message: 
+       u32 type
+       u16 cmd
+       u16 parm0
+       u16 parm1
+       u16 parm2
+       u8  pad[4]
+
+       total: 4 + 2 + 2 + 2 + 2 + 4 = 16
+*/
+static int zd1201_docmd(struct zd1201 *zd, int cmd, int parm0,
+                       int parm1, int parm2)
+{
+       unsigned char *command;
+       int ret;
+       struct urb *urb;
+
+       command = kmalloc(16, GFP_ATOMIC);
+       if (!command)
+               return -ENOMEM;
+
+       *((__le32*)command) = cpu_to_le32(ZD1201_USB_CMDREQ);
+       *((__le16*)&command[4]) = cpu_to_le16(cmd);
+       *((__le16*)&command[6]) = cpu_to_le16(parm0);
+       *((__le16*)&command[8]) = cpu_to_le16(parm1);
+       *((__le16*)&command[10])= cpu_to_le16(parm2);
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb) {
+               kfree(command);
+               return -ENOMEM;
+       }
+       usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2),
+            command, 16, zd1201_usbfree, zd);
+       ret = usb_submit_urb(urb, GFP_ATOMIC);
+       if (ret) {
+               kfree(command);
+               usb_free_urb(urb);
+       }
+       
+       return ret;
+}
+
+/* Callback after sending out a packet */
+static void zd1201_usbtx(struct urb *urb, struct pt_regs *regs)
+{
+       struct zd1201 *zd = urb->context;
+       netif_wake_queue(zd->dev);
+       return;
+}
+
+/* Incoming data */
+static void zd1201_usbrx(struct urb *urb, struct pt_regs *regs)
+{
+       struct zd1201 *zd = urb->context;
+       int free = 0;
+       unsigned char *data = urb->transfer_buffer;
+       struct sk_buff *skb;
+       unsigned char type;
+
+       if (!zd) {
+               free = 1;
+               goto exit;
+       }
+
+       switch(urb->status) {
+               case -EILSEQ:
+               case -ENODEV:
+               case -ETIMEDOUT:
+               case -ENOENT:
+               case -EPIPE:
+               case -EOVERFLOW:
+               case -ESHUTDOWN:
+                       dev_warn(&zd->usb->dev, "%s: rx urb failed: %d\n",
+                           zd->dev->name, urb->status);
+                       free = 1;
+                       goto exit;
+       }
+       
+       if (urb->status != 0 || urb->actual_length == 0)
+               goto resubmit;
+
+       type = data[0];
+       if (type == ZD1201_PACKET_EVENTSTAT || type == ZD1201_PACKET_RESOURCE) {
+               memcpy(zd->rxdata, data, urb->actual_length);
+               zd->rxlen = urb->actual_length;
+               zd->rxdatas = 1;
+               wake_up(&zd->rxdataq);
+       }
+       /* Info frame */
+       if (type == ZD1201_PACKET_INQUIRE) {
+               int i = 0;
+               unsigned short infotype, framelen, copylen;
+               framelen = le16_to_cpu(*(__le16*)&data[4]);
+               infotype = le16_to_cpu(*(__le16*)&data[6]);
+
+               if (infotype == ZD1201_INF_LINKSTATUS) {
+                       short linkstatus;
+
+                       linkstatus = le16_to_cpu(*(__le16*)&data[8]);
+                       switch(linkstatus) {
+                               case 1:
+                                       netif_carrier_on(zd->dev);
+                                       break;
+                               case 2:
+                                       netif_carrier_off(zd->dev);
+                                       break;
+                               case 3:
+                                       netif_carrier_off(zd->dev);
+                                       break;
+                               case 4:
+                                       netif_carrier_on(zd->dev);
+                                       break;
+                               default:
+                                       netif_carrier_off(zd->dev);
+                       }
+                       goto resubmit;
+               }
+               if (infotype == ZD1201_INF_ASSOCSTATUS) {
+                       short status = le16_to_cpu(*(__le16*)(data+8));
+                       int event;
+                       union iwreq_data wrqu;
+
+                       switch (status) {
+                               case ZD1201_ASSOCSTATUS_STAASSOC:
+                               case ZD1201_ASSOCSTATUS_REASSOC:
+                                       event = IWEVREGISTERED;
+                                       break;
+                               case ZD1201_ASSOCSTATUS_DISASSOC:
+                               case ZD1201_ASSOCSTATUS_ASSOCFAIL:
+                               case ZD1201_ASSOCSTATUS_AUTHFAIL:
+                               default:
+                                       event = IWEVEXPIRED;
+                       }
+                       memcpy(wrqu.addr.sa_data, data+10, ETH_ALEN);
+                       wrqu.addr.sa_family = ARPHRD_ETHER;
+
+                       /* Send event to user space */
+                       wireless_send_event(zd->dev, event, &wrqu, NULL);
+
+                       goto resubmit;
+               }
+               if (infotype == ZD1201_INF_AUTHREQ) {
+                       union iwreq_data wrqu;
+
+                       memcpy(wrqu.addr.sa_data, data+8, ETH_ALEN);
+                       wrqu.addr.sa_family = ARPHRD_ETHER;
+                       /* There isn't a event that trully fits this request.
+                          We assume that userspace will be smart enough to
+                          see a new station being expired and sends back a
+                          authstation ioctl to authorize it. */
+                       wireless_send_event(zd->dev, IWEVEXPIRED, &wrqu, NULL);
+                       goto resubmit;
+               }
+               /* Other infotypes are handled outside this handler */
+               zd->rxlen = 0;
+               while (i < urb->actual_length) {
+                       copylen = le16_to_cpu(*(__le16*)&data[i+2]);
+                       /* Sanity check, sometimes we get junk */
+                       if (copylen+zd->rxlen > sizeof(zd->rxdata))
+                               break;
+                       memcpy(zd->rxdata+zd->rxlen, data+i+4, copylen);
+                       zd->rxlen += copylen;
+                       i += 64;
+               }
+               if (i >= urb->actual_length) {
+                       zd->rxdatas = 1;
+                       wake_up(&zd->rxdataq);
+               }
+               goto  resubmit;
+       }
+       /* Actual data */
+       if (data[urb->actual_length-1] == ZD1201_PACKET_RXDATA) {
+               int datalen = urb->actual_length-1;
+               unsigned short len, fc, seq;
+               struct hlist_node *node;
+
+               len = ntohs(*(__be16 *)&data[datalen-2]);
+               if (len>datalen)
+                       len=datalen;
+               fc = le16_to_cpu(*(__le16 *)&data[datalen-16]);
+               seq = le16_to_cpu(*(__le16 *)&data[datalen-24]);
+
+               if(zd->monitor) {
+                       if (datalen < 24)
+                               goto resubmit;
+                       if (!(skb = dev_alloc_skb(datalen+24)))
+                               goto resubmit;
+                       
+                       memcpy(skb_put(skb, 2), &data[datalen-16], 2);
+                       memcpy(skb_put(skb, 2), &data[datalen-2], 2);
+                       memcpy(skb_put(skb, 6), &data[datalen-14], 6);
+                       memcpy(skb_put(skb, 6), &data[datalen-22], 6);
+                       memcpy(skb_put(skb, 6), &data[datalen-8], 6);
+                       memcpy(skb_put(skb, 2), &data[datalen-24], 2);
+                       memcpy(skb_put(skb, len), data, len);
+                       skb->dev = zd->dev;
+                       skb->dev->last_rx = jiffies;
+                       skb->protocol = eth_type_trans(skb, zd->dev);
+                       zd->stats.rx_packets++;
+                       zd->stats.rx_bytes += skb->len;
+                       netif_rx(skb);
+                       goto resubmit;
+               }
+                       
+               if ((seq & IEEE802_11_SCTL_FRAG) ||
+                   (fc & IEEE802_11_FCTL_MOREFRAGS)) {
+                       struct zd1201_frag *frag = NULL;
+                       char *ptr;
+
+                       if (datalen<14)
+                               goto resubmit;
+                       if ((seq & IEEE802_11_SCTL_FRAG) == 0) {
+                               frag = kmalloc(sizeof(struct zd1201_frag*),
+                                   GFP_ATOMIC);
+                               if (!frag)
+                                       goto resubmit;
+                               skb = dev_alloc_skb(IEEE802_11_DATA_LEN +14+2);
+                               if (!skb) {
+                                       kfree(frag);
+                                       goto resubmit;
+                               }
+                               frag->skb = skb;
+                               frag->seq = seq & IEEE802_11_SCTL_SEQ;
+                               skb_reserve(skb, 2);
+                               memcpy(skb_put(skb, 12), &data[datalen-14], 12);
+                               memcpy(skb_put(skb, 2), &data[6], 2);
+                               memcpy(skb_put(skb, len), data+8, len);
+                               hlist_add_head(&frag->fnode, &zd->fraglist);
+                               goto resubmit;
+                       }
+                       hlist_for_each_entry(frag, node, &zd->fraglist, fnode)
+                               if(frag->seq == (seq&IEEE802_11_SCTL_SEQ))
+                                       break;
+                       if (!frag)
+                               goto resubmit;
+                       skb = frag->skb;
+                       ptr = skb_put(skb, len);
+                       if (ptr)
+                               memcpy(ptr, data+8, len);
+                       if (fc & IEEE802_11_FCTL_MOREFRAGS)
+                               goto resubmit;
+                       hlist_del_init(&frag->fnode);
+                       kfree(frag);
+                       /* Fallthrough */
+               } else {
+                       if (datalen<14)
+                               goto resubmit;
+                       skb = dev_alloc_skb(len + 14 + 2);
+                       if (!skb)
+                               goto resubmit;
+                       skb_reserve(skb, 2);
+                       memcpy(skb_put(skb, 12), &data[datalen-14], 12);
+                       memcpy(skb_put(skb, 2), &data[6], 2);
+                       memcpy(skb_put(skb, len), data+8, len);
+               }
+               skb->dev = zd->dev;
+               skb->dev->last_rx = jiffies;
+               skb->protocol = eth_type_trans(skb, zd->dev);
+               zd->stats.rx_packets++;
+               zd->stats.rx_bytes += skb->len;
+               netif_rx(skb);
+       }
+resubmit:
+       memset(data, 0, ZD1201_RXSIZE);
+
+       urb->status = 0;
+       urb->dev = zd->usb;
+       if(usb_submit_urb(urb, GFP_ATOMIC))
+               free = 1;
+
+exit:
+       if (free) {
+               zd->rxlen = 0;
+               zd->rxdatas = 1;
+               wake_up(&zd->rxdataq);
+               kfree(urb->transfer_buffer);
+       }
+       return;
+}
+
+static int zd1201_getconfig(struct zd1201 *zd, int rid, void *riddata,
+       unsigned int riddatalen)
+{
+       int err;
+       int i = 0;
+       int code;
+       int rid_fid;
+       int length;
+       unsigned char *pdata;
+       
+       zd->rxdatas = 0;
+       err = zd1201_docmd(zd, ZD1201_CMDCODE_ACCESS, rid, 0, 0);
+       if (err)
+               return err;
+
+       wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+       if (!zd->rxlen)
+               return -EIO;
+
+       code = le16_to_cpu(*(__le16*)(&zd->rxdata[4]));
+       rid_fid = le16_to_cpu(*(__le16*)(&zd->rxdata[6]));
+       length = le16_to_cpu(*(__le16*)(&zd->rxdata[8]));
+       if (length > zd->rxlen)
+               length = zd->rxlen-6;
+
+       /* If access bit is not on, then error */
+       if ((code & ZD1201_ACCESSBIT) != ZD1201_ACCESSBIT || rid_fid != rid )
+               return -EINVAL;
+
+       /* Not enough buffer for allocating data */
+       if (riddatalen != (length - 4)) {
+               dev_dbg(&zd->usb->dev, "riddatalen mismatches, expected=%u, (packet=%u) length=%u, rid=0x%04X, rid_fid=0x%04X\n",
+                   riddatalen, zd->rxlen, length, rid, rid_fid);
+               return -ENODATA;
+       }
+
+       zd->rxdatas = 0;
+       /* Issue SetRxRid commnd */                     
+       err = zd1201_docmd(zd, ZD1201_CMDCODE_SETRXRID, rid, 0, length);
+       if (err)
+               return err;
+
+       /* Receive RID record from resource packets */
+       wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+       if (!zd->rxlen)
+               return -EIO;
+
+       if (zd->rxdata[zd->rxlen - 1] != ZD1201_PACKET_RESOURCE) {
+               dev_dbg(&zd->usb->dev, "Packet type mismatch: 0x%x not 0x3\n",
+                   zd->rxdata[zd->rxlen-1]);
+               return -EINVAL;
+       }
+
+       /* Set the data pointer and received data length */
+       pdata = zd->rxdata;
+       length = zd->rxlen;
+
+       do {
+               int  actual_length;
+
+               actual_length = (length > 64) ? 64 : length;
+
+               if(pdata[0] != 0x3) {
+                       dev_dbg(&zd->usb->dev, "Rx Resource packet type error: %02X\n",
+                           pdata[0]);
+                       return -EINVAL;
+               }
+
+               if (actual_length != 64) {
+                       /* Trim the last packet type byte */
+                       actual_length--;
+               }
+
+               /* Skip the 4 bytes header (RID length and RID) */
+               if(i == 0) {
+                       pdata += 8;
+                       actual_length -= 8;
+               }
+               else {
+                       pdata += 4;
+                       actual_length -= 4;
+               }
+               
+               memcpy(riddata, pdata, actual_length);
+               riddata += actual_length;
+               pdata += actual_length;
+               length -= 64;
+               i++;
+       } while (length > 0);
+
+       return 0;
+}
+
+/*
+ *     resreq:
+ *             byte    type
+ *             byte    sequence
+ *             u16     reserved
+ *             byte    data[12]
+ *     total: 16
+ */
+static int zd1201_setconfig(struct zd1201 *zd, int rid, void *buf, int len, int wait)
+{
+       int err;
+       unsigned char *request;
+       int reqlen;
+       char seq=0;
+       struct urb *urb;
+       unsigned int gfp_mask = wait ? GFP_NOIO : GFP_ATOMIC;
+
+       len += 4;                       /* first 4 are for header */
+
+       zd->rxdatas = 0;
+       zd->rxlen = 0;
+       for (seq=0; len > 0; seq++) {
+               request = kmalloc(16, gfp_mask);
+               if (!request)
+                       return -ENOMEM;
+               urb = usb_alloc_urb(0, gfp_mask);
+               if (!urb) {
+                       kfree(request);
+                       return -ENOMEM;
+               }
+               memset(request, 0, 16);
+               reqlen = len>12 ? 12 : len;
+               request[0] = ZD1201_USB_RESREQ;
+               request[1] = seq;
+               request[2] = 0;
+               request[3] = 0;
+               if (request[1] == 0) {
+                       /* add header */
+                       *(__le16*)&request[4] = cpu_to_le16((len-2+1)/2);
+                       *(__le16*)&request[6] = cpu_to_le16(rid);
+                       memcpy(request+8, buf, reqlen-4);
+                       buf += reqlen-4;
+               } else {
+                       memcpy(request+4, buf, reqlen);
+                       buf += reqlen;
+               }
+
+               len -= reqlen;
+
+               usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb,
+                   zd->endp_out2), request, 16, zd1201_usbfree, zd);
+               err = usb_submit_urb(urb, gfp_mask);
+               if (err)
+                       goto err;
+       }
+
+       request = kmalloc(16, gfp_mask);
+       if (!request)
+               return -ENOMEM;
+       urb = usb_alloc_urb(0, gfp_mask);
+       if (!urb) {
+               kfree(request);
+               return -ENOMEM;
+       }
+       *((__le32*)request) = cpu_to_le32(ZD1201_USB_CMDREQ);
+       *((__le16*)&request[4]) = 
+           cpu_to_le16(ZD1201_CMDCODE_ACCESS|ZD1201_ACCESSBIT);
+       *((__le16*)&request[6]) = cpu_to_le16(rid);
+       *((__le16*)&request[8]) = cpu_to_le16(0);
+       *((__le16*)&request[10]) = cpu_to_le16(0);
+       usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2),
+            request, 16, zd1201_usbfree, zd);
+       err = usb_submit_urb(urb, gfp_mask);
+       if (err)
+               goto err;
+       
+       if (wait) {
+               wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+               if (!zd->rxlen || le16_to_cpu(*(__le16*)&zd->rxdata[6]) != rid) {
+                       dev_dbg(&zd->usb->dev, "wrong or no RID received\n");
+               }
+       }
+
+       return 0;
+err:
+       kfree(request);
+       usb_free_urb(urb);
+       return err;
+}
+
+static inline int zd1201_getconfig16(struct zd1201 *zd, int rid, short *val)
+{
+       int err;
+       __le16 zdval;
+
+       err = zd1201_getconfig(zd, rid, &zdval, sizeof(__le16));
+       if (err)
+               return err;
+       *val = le16_to_cpu(zdval);
+       return 0;
+}
+
+static inline int zd1201_setconfig16(struct zd1201 *zd, int rid, short val)
+{
+       __le16 zdval = cpu_to_le16(val);
+       return (zd1201_setconfig(zd, rid, &zdval, sizeof(__le16), 1));
+}
+
+static int zd1201_drvr_start(struct zd1201 *zd)
+{
+       int err, i;
+       short max;
+       __le16 zdmax;
+       unsigned char *buffer;
+       
+       buffer = kmalloc(ZD1201_RXSIZE, GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+       memset(buffer, 0, ZD1201_RXSIZE);
+
+       usb_fill_bulk_urb(zd->rx_urb, zd->usb, 
+           usb_rcvbulkpipe(zd->usb, zd->endp_in), buffer, ZD1201_RXSIZE,
+           zd1201_usbrx, zd);
+
+       err = usb_submit_urb(zd->rx_urb, GFP_KERNEL);
+       if (err)
+               goto err_buffer;
+       
+       err = zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0);
+       if (err)
+               goto err_urb;
+
+       err = zd1201_getconfig(zd, ZD1201_RID_CNFMAXTXBUFFERNUMBER, &zdmax,
+           sizeof(__le16));
+       if (err)
+               goto err_urb;
+
+       max = le16_to_cpu(zdmax);
+       for (i=0; i<max; i++) {
+               err = zd1201_docmd(zd, ZD1201_CMDCODE_ALLOC, 1514, 0, 0);
+               if (err)
+                       goto err_urb;
+       }
+
+       return 0;
+
+err_urb:
+       usb_kill_urb(zd->rx_urb);
+       return err;
+err_buffer:
+       kfree(buffer);
+       return err;
+}
+
+/*     Magic alert: The firmware doesn't seem to like the MAC state being
+ *     toggled in promisc (aka monitor) mode.
+ *     (It works a number of times, but will halt eventually)
+ *     So we turn it of before disabling and on after enabling if needed.
+ */
+static int zd1201_enable(struct zd1201 *zd)
+{
+       int err;
+
+       if (zd->mac_enabled)
+               return 0;
+
+       err = zd1201_docmd(zd, ZD1201_CMDCODE_ENABLE, 0, 0, 0);
+       if (!err)
+               zd->mac_enabled = 1;
+
+       if (zd->monitor)
+               err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 1);
+
+       return err;
+}
+
+static int zd1201_disable(struct zd1201 *zd)
+{
+       int err;
+       
+       if (!zd->mac_enabled)
+               return 0;
+       if (zd->monitor) {
+               err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 0);
+               if (err)
+                       return err;
+       }
+
+       err = zd1201_docmd(zd, ZD1201_CMDCODE_DISABLE, 0, 0, 0);
+       if (!err)
+               zd->mac_enabled = 0;
+       return err;
+}
+
+static int zd1201_mac_reset(struct zd1201 *zd)
+{
+       if (!zd->mac_enabled)
+               return 0;
+       zd1201_disable(zd);
+       return zd1201_enable(zd);
+}
+
+static int zd1201_join(struct zd1201 *zd, char *essid, int essidlen)
+{
+       int err, val;
+       char buf[IW_ESSID_MAX_SIZE+2];
+
+       err = zd1201_disable(zd);
+       if (err)
+               return err;
+
+       val = ZD1201_CNFAUTHENTICATION_OPENSYSTEM;
+       val |= ZD1201_CNFAUTHENTICATION_SHAREDKEY;
+       err = zd1201_setconfig16(zd, ZD1201_RID_CNFAUTHENTICATION, val);
+       if (err)
+               return err;
+
+       *(__le16 *)buf = cpu_to_le16(essidlen);
+       memcpy(buf+2, essid, essidlen);
+       if (!zd->ap) {  /* Normal station */
+               err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf,
+                   IW_ESSID_MAX_SIZE+2, 1);
+               if (err)
+                       return err;
+       } else {        /* AP */
+               err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNSSID, buf,
+                   IW_ESSID_MAX_SIZE+2, 1);
+               if (err)
+                       return err;
+       }
+
+       err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNMACADDR, 
+           zd->dev->dev_addr, zd->dev->addr_len, 1);
+       if (err)
+               return err;
+
+       err = zd1201_enable(zd);
+       if (err)
+               return err;
+
+       msleep(100);
+       return 0;
+}
+
+static int zd1201_net_open(struct net_device *dev)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+       /* Start MAC with wildcard if no essid set */
+       if (!zd->mac_enabled)
+               zd1201_join(zd, zd->essid, zd->essidlen);
+       netif_start_queue(dev);
+
+       return 0;
+}
+
+static int zd1201_net_stop(struct net_device *dev)
+{
+       netif_stop_queue(dev);
+       
+       return 0;
+}
+
+/*
+       RFC 1042 encapsulates Ethernet frames in 802.11 frames
+       by prefixing them with 0xaa, 0xaa, 0x03) followed by a SNAP OID of 0
+       (0x00, 0x00, 0x00). Zd requires an additional padding, copy
+       of ethernet addresses, length of the standard RFC 1042 packet
+       and a command byte (which is nul for tx).
+       
+       tx frame (from Wlan NG):
+       RFC 1042:
+               llc             0xAA 0xAA 0x03 (802.2 LLC)
+               snap            0x00 0x00 0x00 (Ethernet encapsulated)
+               type            2 bytes, Ethernet type field
+               payload         (minus eth header)
+       Zydas specific:
+               padding         1B if (skb->len+8+1)%64==0
+               Eth MAC addr    12 bytes, Ethernet MAC addresses
+               length          2 bytes, RFC 1042 packet length 
+                               (llc+snap+type+payload)
+               zd              1 null byte, zd1201 packet type
+ */
+static int zd1201_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       unsigned char *txbuf = zd->txdata;
+       int txbuflen, pad = 0, err;
+       struct urb *urb = zd->tx_urb;
+
+       if (!zd->mac_enabled || zd->monitor) {
+               zd->stats.tx_dropped++;
+               kfree_skb(skb);
+               return 0;
+       }
+       netif_stop_queue(dev);
+
+       txbuflen = skb->len + 8 + 1;
+       if (txbuflen%64 == 0) {
+               pad = 1;
+               txbuflen++;
+       }
+       txbuf[0] = 0xAA;
+       txbuf[1] = 0xAA;
+       txbuf[2] = 0x03;
+       txbuf[3] = 0x00;        /* rfc1042 */
+       txbuf[4] = 0x00;
+       txbuf[5] = 0x00;
+
+       memcpy(txbuf+6, skb->data+12, skb->len-12);
+       if (pad)
+               txbuf[skb->len-12+6]=0;
+       memcpy(txbuf+skb->len-12+6+pad, skb->data, 12);
+       *(__be16*)&txbuf[skb->len+6+pad] = htons(skb->len-12+6);
+       txbuf[txbuflen-1] = 0;
+
+       usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out),
+           txbuf, txbuflen, zd1201_usbtx, zd);
+
+       err = usb_submit_urb(zd->tx_urb, GFP_ATOMIC);
+       if (err) {
+               zd->stats.tx_errors++;
+               netif_start_queue(dev);
+               return err;
+       }
+       zd->stats.tx_packets++;
+       zd->stats.tx_bytes += skb->len;
+       dev->trans_start = jiffies;
+       kfree_skb(skb);
+
+       return 0;
+}
+
+static void zd1201_tx_timeout(struct net_device *dev)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+       if (!zd)
+               return;
+       dev_warn(&zd->usb->dev, "%s: TX timeout, shooting down urb\n",
+           dev->name);
+       zd->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
+       usb_unlink_urb(zd->tx_urb);
+       zd->stats.tx_errors++;
+       /* Restart the timeout to quiet the watchdog: */
+       dev->trans_start = jiffies;
+}
+
+static int zd1201_set_mac_address(struct net_device *dev, void *p)
+{
+       struct sockaddr *addr = p;
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       int err;
+
+       if (!zd)
+               return -ENODEV;
+
+       err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNMACADDR, 
+           addr->sa_data, dev->addr_len, 1);
+       if (err)
+               return err;
+       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+       return zd1201_mac_reset(zd);
+}
+
+static struct net_device_stats *zd1201_get_stats(struct net_device *dev)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+       return &zd->stats;
+}
+
+static struct iw_statistics *zd1201_get_wireless_stats(struct net_device *dev)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+       return &zd->iwstats;
+}
+
+static void zd1201_set_multicast(struct net_device *dev)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       struct dev_mc_list *mc = dev->mc_list;
+       unsigned char reqbuf[ETH_ALEN*ZD1201_MAXMULTI];
+       int i;
+
+       if (dev->mc_count > ZD1201_MAXMULTI)
+               return;
+
+       for (i=0; i<dev->mc_count; i++) {
+               memcpy(reqbuf+i*ETH_ALEN, mc->dmi_addr, ETH_ALEN);
+               mc = mc->next;
+       }
+       zd1201_setconfig(zd, ZD1201_RID_CNFGROUPADDRESS, reqbuf,
+           dev->mc_count*ETH_ALEN, 0);
+       
+}
+
+static int zd1201_config_commit(struct net_device *dev, 
+    struct iw_request_info *info, struct iw_point *data, char *essid)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+       return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_name(struct net_device *dev,
+    struct iw_request_info *info, char *name, char *extra)
+{
+       strcpy(name, "IEEE 802.11b");
+
+       return 0;
+}
+
+static int zd1201_set_freq(struct net_device *dev,
+    struct iw_request_info *info, struct iw_freq *freq, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       short channel = 0;
+       int err;
+
+       if (freq->e == 0)
+               channel = freq->m;
+       else {
+               if (freq->m >= 2482)
+                       channel = 14;
+               if (freq->m >= 2407)
+                       channel = (freq->m-2407)/5;
+       }
+
+       err = zd1201_setconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, channel);
+       if (err)
+               return err;
+
+       zd1201_mac_reset(zd);
+
+       return 0;
+}
+
+static int zd1201_get_freq(struct net_device *dev,
+    struct iw_request_info *info, struct iw_freq *freq, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       short channel;
+       int err;
+
+       err = zd1201_getconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, &channel);
+       if (err)
+               return err;
+       freq->e = 0;
+       freq->m = channel;
+
+       return 0;
+}
+
+static int zd1201_set_mode(struct net_device *dev,
+    struct iw_request_info *info, __u32 *mode, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       short porttype, monitor = 0;
+       unsigned char buffer[IW_ESSID_MAX_SIZE+2];
+       int err;
+
+       if (zd->ap) {
+               if (*mode != IW_MODE_MASTER)
+                       return -EINVAL;
+               return 0;
+       }
+
+       err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 0);
+       if (err)
+               return err;
+       zd->dev->type = ARPHRD_ETHER;
+       switch(*mode) {
+               case IW_MODE_MONITOR:
+                       monitor = 1;
+                       zd->dev->type = ARPHRD_IEEE80211;
+                       /* Make sure we are no longer associated with by
+                          setting an 'impossible' essid.
+                          (otherwise we mess up firmware)
+                        */
+                       zd1201_join(zd, "\0-*#\0", 5);
+                       /* Put port in pIBSS */
+               case 8: /* No pseudo-IBSS in wireless extensions (yet) */
+                       porttype = ZD1201_PORTTYPE_PSEUDOIBSS;
+                       break;
+               case IW_MODE_ADHOC:
+                       porttype = ZD1201_PORTTYPE_IBSS;
+                       break;
+               case IW_MODE_INFRA:
+                       porttype = ZD1201_PORTTYPE_BSS;
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype);
+       if (err)
+               return err;
+       if (zd->monitor && !monitor) {
+                       zd1201_disable(zd);
+                       *(__le16 *)buffer = cpu_to_le16(zd->essidlen);
+                       memcpy(buffer+2, zd->essid, zd->essidlen);
+                       err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID,
+                           buffer, IW_ESSID_MAX_SIZE+2, 1);
+                       if (err)
+                               return err;
+       }
+       zd->monitor=monitor;
+       /* If monitor mode is set we don't actually turn it on here since it
+        * is done during mac reset anyway (see zd1201_mac_enable).
+        */
+
+       zd1201_mac_reset(zd);
+
+       return 0;
+}
+
+static int zd1201_get_mode(struct net_device *dev,
+    struct iw_request_info *info, __u32 *mode, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       short porttype;
+       int err;
+
+       err = zd1201_getconfig16(zd, ZD1201_RID_CNFPORTTYPE, &porttype);
+       if (err)
+               return err;
+       switch(porttype) {
+               case ZD1201_PORTTYPE_IBSS:
+                       *mode = IW_MODE_ADHOC;
+                       break;
+               case ZD1201_PORTTYPE_BSS:
+                       *mode = IW_MODE_INFRA;
+                       break;
+               case ZD1201_PORTTYPE_WDS:
+                       *mode = IW_MODE_REPEAT;
+                       break;
+               case ZD1201_PORTTYPE_PSEUDOIBSS:
+                       *mode = 8;/* No Pseudo-IBSS... */
+                       break;
+               case ZD1201_PORTTYPE_AP:
+                       *mode = IW_MODE_MASTER;
+                       break;
+               default:
+                       dev_dbg(&zd->usb->dev, "Unknown porttype: %d\n",
+                           porttype);
+                       *mode = IW_MODE_AUTO;
+       }
+       if (zd->monitor)
+               *mode = IW_MODE_MONITOR;
+
+       return 0;
+}
+
+static int zd1201_get_range(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *wrq, char *extra)
+{
+       struct iw_range *range = (struct iw_range *)extra;
+
+       wrq->length = sizeof(struct iw_range);
+       memset(range, 0, sizeof(struct iw_range));
+       range->we_version_compiled = WIRELESS_EXT;
+       range->we_version_source = WIRELESS_EXT;
+
+       range->max_qual.qual = 128;
+       range->max_qual.level = 128;
+       range->max_qual.noise = 128;
+       range->max_qual.updated = 7;
+
+       range->encoding_size[0] = 5;
+       range->encoding_size[1] = 13;
+       range->num_encoding_sizes = 2;
+       range->max_encoding_tokens = ZD1201_NUMKEYS;
+
+       range->num_bitrates = 4;
+       range->bitrate[0] = 1000000;
+       range->bitrate[1] = 2000000;
+       range->bitrate[2] = 5500000;
+       range->bitrate[3] = 11000000;
+
+       range->min_rts = 0;
+       range->min_frag = ZD1201_FRAGMIN;
+       range->max_rts = ZD1201_RTSMAX;
+       range->min_frag = ZD1201_FRAGMAX;
+
+       return 0;
+}
+
+/*     Little bit of magic here: we only get the quality if we poll
+ *     for it, and we never get an actual request to trigger such
+ *     a poll. Therefore we 'assume' that the user will soon ask for
+ *     the stats after asking the bssid.
+ */
+static int zd1201_get_wap(struct net_device *dev,
+    struct iw_request_info *info, struct sockaddr *ap_addr, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       unsigned char buffer[6];
+
+       if (!zd1201_getconfig(zd, ZD1201_RID_COMMSQUALITY, buffer, 6)) {
+               /* Unfortunately the quality and noise reported is useless.
+                  they seem to be accumulators that increase until you
+                  read them, unless we poll on a fixed interval we can't
+                  use them
+                */
+               /*zd->iwstats.qual.qual = le16_to_cpu(((__le16 *)buffer)[0]);*/
+               zd->iwstats.qual.level = le16_to_cpu(((__le16 *)buffer)[1]);
+               /*zd->iwstats.qual.noise = le16_to_cpu(((__le16 *)buffer)[2]);*/
+               zd->iwstats.qual.updated = 2;
+       }
+
+       return zd1201_getconfig(zd,ZD1201_RID_CURRENTBSSID,ap_addr->sa_data,6);
+}
+
+static int zd1201_set_scan(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *srq, char *extra)
+{
+       /* We do everything in get_scan */
+       return 0;
+}
+
+static int zd1201_get_scan(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *srq, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       int err, i, j, enabled_save;
+       struct iw_event iwe;
+       char *cev = extra;
+       char *end_buf = extra + IW_SCAN_MAX_DATA;
+
+       /* No scanning in AP mode */
+       if (zd->ap)
+               return -EOPNOTSUPP;
+
+       /* Scan doesn't seem to work if disabled */
+       enabled_save = zd->mac_enabled;
+       zd1201_enable(zd);
+
+       zd->rxdatas = 0;
+       err = zd1201_docmd(zd, ZD1201_CMDCODE_INQUIRE, 
+            ZD1201_INQ_SCANRESULTS, 0, 0);
+       if (err)
+               return err;
+
+       wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+       if (!zd->rxlen)
+               return -EIO;
+
+       if (le16_to_cpu(*(__le16*)&zd->rxdata[2]) != ZD1201_INQ_SCANRESULTS)
+               return -EIO;
+
+       for(i=8; i<zd->rxlen; i+=62) {
+               iwe.cmd = SIOCGIWAP;
+               iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+               memcpy(iwe.u.ap_addr.sa_data, zd->rxdata+i+6, 6);
+               cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_ADDR_LEN);
+
+               iwe.cmd = SIOCGIWESSID;
+               iwe.u.data.length = zd->rxdata[i+16];
+               iwe.u.data.flags = 1;
+               cev = iwe_stream_add_point(cev, end_buf, &iwe, zd->rxdata+i+18);
+
+               iwe.cmd = SIOCGIWMODE;
+               if (zd->rxdata[i+14]&0x01)
+                       iwe.u.mode = IW_MODE_MASTER;
+               else
+                       iwe.u.mode = IW_MODE_ADHOC;
+               cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_UINT_LEN);
+               
+               iwe.cmd = SIOCGIWFREQ;
+               iwe.u.freq.m = zd->rxdata[i+0];
+               iwe.u.freq.e = 0;
+               cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_FREQ_LEN);
+               
+               iwe.cmd = SIOCGIWRATE;
+               iwe.u.bitrate.fixed = 0;
+               iwe.u.bitrate.disabled = 0;
+               for (j=0; j<10; j++) if (zd->rxdata[i+50+j]) {
+                       iwe.u.bitrate.value = (zd->rxdata[i+50+j]&0x7f)*500000;
+                       cev=iwe_stream_add_event(cev, end_buf, &iwe,
+                           IW_EV_PARAM_LEN);
+               }
+               
+               iwe.cmd = SIOCGIWENCODE;
+               iwe.u.data.length = 0;
+               if (zd->rxdata[i+14]&0x10)
+                       iwe.u.data.flags = IW_ENCODE_ENABLED;
+               else
+                       iwe.u.data.flags = IW_ENCODE_DISABLED;
+               cev = iwe_stream_add_point(cev, end_buf, &iwe, NULL);
+               
+               iwe.cmd = IWEVQUAL;
+               iwe.u.qual.qual = zd->rxdata[i+4];
+               iwe.u.qual.noise= zd->rxdata[i+2]/10-100;
+               iwe.u.qual.level = (256+zd->rxdata[i+4]*100)/255-100;
+               iwe.u.qual.updated = 7;
+               cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_QUAL_LEN);
+       }
+
+       if (!enabled_save)
+               zd1201_disable(zd);
+
+       srq->length = cev - extra;
+       srq->flags = 0;
+
+       return 0;
+}
+
+static int zd1201_set_essid(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *data, char *essid)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+       if (data->length > IW_ESSID_MAX_SIZE)
+               return -EINVAL;
+       if (data->length < 1)
+               data->length = 1;
+       zd->essidlen = data->length-1;
+       memset(zd->essid, 0, IW_ESSID_MAX_SIZE+1);
+       memcpy(zd->essid, essid, data->length);
+       return zd1201_join(zd, zd->essid, zd->essidlen);
+}
+
+static int zd1201_get_essid(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *data, char *essid)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+       memcpy(essid, zd->essid, zd->essidlen);
+       data->flags = 1;
+       data->length = zd->essidlen;
+
+       return 0;
+}
+
+static int zd1201_get_nick(struct net_device *dev, struct iw_request_info *info,
+    struct iw_point *data, char *nick)
+{
+       strcpy(nick, "zd1201");
+       data->flags = 1;
+       data->length = strlen(nick);
+       return 0;
+}
+
+static int zd1201_set_rate(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       short rate;
+       int err;
+
+       switch (rrq->value) {
+               case 1000000:
+                       rate = ZD1201_RATEB1;
+                       break;
+               case 2000000:
+                       rate = ZD1201_RATEB2;
+                       break;
+               case 5500000:
+                       rate = ZD1201_RATEB5;
+                       break;
+               case 11000000:
+               default:
+                       rate = ZD1201_RATEB11;
+                       break;
+       }
+       if (!rrq->fixed) { /* Also enable all lower bitrates */
+               rate |= rate-1;
+       }
+       
+       err = zd1201_setconfig16(zd, ZD1201_RID_TXRATECNTL, rate);
+       if (err)
+               return err;
+
+       return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_rate(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       short rate;
+       int err;
+
+       err = zd1201_getconfig16(zd, ZD1201_RID_CURRENTTXRATE, &rate);
+       if (err)
+               return err;
+
+       switch(rate) {
+               case 1:
+                       rrq->value = 1000000;
+                       break;
+               case 2:
+                       rrq->value = 2000000;
+                       break;
+               case 5:
+                       rrq->value = 5500000;
+                       break;
+               case 11:
+                       rrq->value = 11000000;
+                       break;
+               default:
+                       rrq->value = 0;
+       }
+       rrq->fixed = 0;
+       rrq->disabled = 0;
+
+       return 0;
+}
+
+static int zd1201_set_rts(struct net_device *dev, struct iw_request_info *info,
+    struct iw_param *rts, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       int err;
+       short val = rts->value;
+
+       if (rts->disabled || !rts->fixed)
+               val = ZD1201_RTSMAX;
+       if (val > ZD1201_RTSMAX)
+               return -EINVAL;
+       if (val < 0)
+               return -EINVAL;
+
+       err = zd1201_setconfig16(zd, ZD1201_RID_CNFRTSTHRESHOLD, val);
+       if (err)
+               return err;
+       return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_rts(struct net_device *dev, struct iw_request_info *info,
+    struct iw_param *rts, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       short rtst;
+       int err;
+
+       err = zd1201_getconfig16(zd, ZD1201_RID_CNFRTSTHRESHOLD, &rtst);
+       if (err)
+               return err;
+       rts->value = rtst;
+       rts->disabled = (rts->value == ZD1201_RTSMAX);
+       rts->fixed = 1;
+
+       return 0;
+}
+
+static int zd1201_set_frag(struct net_device *dev, struct iw_request_info *info,
+    struct iw_param *frag, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       int err;
+       short val = frag->value;
+
+       if (frag->disabled || !frag->fixed)
+               val = ZD1201_FRAGMAX;
+       if (val > ZD1201_FRAGMAX)
+               return -EINVAL;
+       if (val < ZD1201_FRAGMIN)
+               return -EINVAL;
+       if (val & 1)
+               return -EINVAL;
+       err = zd1201_setconfig16(zd, ZD1201_RID_CNFFRAGTHRESHOLD, val);
+       if (err)
+               return err;
+       return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_frag(struct net_device *dev, struct iw_request_info *info,
+    struct iw_param *frag, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       short fragt;
+       int err;
+
+       err = zd1201_getconfig16(zd, ZD1201_RID_CNFFRAGTHRESHOLD, &fragt);
+       if (err)
+               return err;
+       frag->value = fragt;
+       frag->disabled = (frag->value == ZD1201_FRAGMAX);
+       frag->fixed = 1;
+
+       return 0;
+}
+
+static int zd1201_set_retry(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+       return 0;
+}
+
+static int zd1201_get_retry(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+       return 0;
+}
+
+static int zd1201_set_encode(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *erq, char *key)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       short i;
+       int err, rid;
+
+       if (erq->length > ZD1201_MAXKEYLEN)
+               return -EINVAL;
+
+       i = (erq->flags & IW_ENCODE_INDEX)-1;
+       if (i == -1) {
+               err = zd1201_getconfig16(zd,ZD1201_RID_CNFDEFAULTKEYID,&i);
+               if (err)
+                       return err;
+       } else {
+               err = zd1201_setconfig16(zd, ZD1201_RID_CNFDEFAULTKEYID, i);
+               if (err)
+                       return err;
+       }
+
+       if (i < 0 || i >= ZD1201_NUMKEYS)
+               return -EINVAL;
+
+       rid = ZD1201_RID_CNFDEFAULTKEY0 + i;
+       err = zd1201_setconfig(zd, rid, key, erq->length, 1);
+       if (err)
+               return err;
+       zd->encode_keylen[i] = erq->length;
+       memcpy(zd->encode_keys[i], key, erq->length);
+
+       i=0;
+       if (!(erq->flags & IW_ENCODE_DISABLED & IW_ENCODE_MODE)) {
+               i |= 0x01;
+               zd->encode_enabled = 1;
+       } else
+               zd->encode_enabled = 0;
+       if (erq->flags & IW_ENCODE_RESTRICTED & IW_ENCODE_MODE) {
+               i |= 0x02;
+               zd->encode_restricted = 1;
+       } else
+               zd->encode_restricted = 0;
+       err = zd1201_setconfig16(zd, ZD1201_RID_CNFWEBFLAGS, i);
+       if (err)
+               return err;
+
+       if (zd->encode_enabled)
+               i = ZD1201_CNFAUTHENTICATION_SHAREDKEY;
+       else
+               i = ZD1201_CNFAUTHENTICATION_OPENSYSTEM;
+       err = zd1201_setconfig16(zd, ZD1201_RID_CNFAUTHENTICATION, i);
+       if (err)
+               return err;
+
+       return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_encode(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *erq, char *key)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       short i;
+       int err;
+
+       if (zd->encode_enabled)
+               erq->flags = IW_ENCODE_ENABLED;
+       else
+               erq->flags = IW_ENCODE_DISABLED;
+       if (zd->encode_restricted)
+               erq->flags |= IW_ENCODE_RESTRICTED;
+       else
+               erq->flags |= IW_ENCODE_OPEN;
+
+       i = (erq->flags & IW_ENCODE_INDEX) -1;
+       if (i == -1) {
+               err = zd1201_getconfig16(zd, ZD1201_RID_CNFDEFAULTKEYID, &i);
+               if (err)
+                       return err;
+       }
+       if (i<0 || i>= ZD1201_NUMKEYS)
+               return -EINVAL;
+
+       erq->flags |= i+1;
+       
+       erq->length = zd->encode_keylen[i];
+       memcpy(key, zd->encode_keys[i], erq->length);
+
+       return 0;
+}
+
+static int zd1201_set_power(struct net_device *dev, 
+    struct iw_request_info *info, struct iw_param *vwrq, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       short enabled, duration, level;
+       int err;
+
+       enabled = vwrq->disabled ? 0 : 1;
+       if (enabled) {
+               if (vwrq->flags & IW_POWER_PERIOD) {
+                       duration = vwrq->value;
+                       err = zd1201_setconfig16(zd, 
+                           ZD1201_RID_CNFMAXSLEEPDURATION, duration);
+                       if (err)
+                               return err;
+                       goto out;
+               }
+               if (vwrq->flags & IW_POWER_TIMEOUT) {
+                       err = zd1201_getconfig16(zd, 
+                           ZD1201_RID_CNFMAXSLEEPDURATION, &duration);
+                       if (err)
+                               return err;
+                       level = vwrq->value * 4 / duration;
+                       if (level > 4)
+                               level = 4;
+                       if (level < 0)
+                               level = 0;
+                       err = zd1201_setconfig16(zd, ZD1201_RID_CNFPMEPS,
+                           level);
+                       if (err)
+                               return err;
+                       goto out;
+               }
+               return -EINVAL;
+       }
+out:
+       err = zd1201_setconfig16(zd, ZD1201_RID_CNFPMENABLED, enabled);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int zd1201_get_power(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *vwrq, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       short enabled, level, duration;
+       int err;
+
+       err = zd1201_getconfig16(zd, ZD1201_RID_CNFPMENABLED, &enabled);
+       if (err)
+               return err;
+       err = zd1201_getconfig16(zd, ZD1201_RID_CNFPMEPS, &level);
+       if (err)
+               return err;
+       err = zd1201_getconfig16(zd, ZD1201_RID_CNFMAXSLEEPDURATION, &duration);
+       if (err)
+               return err;
+       vwrq->disabled = enabled ? 0 : 1;
+       if (vwrq->flags & IW_POWER_TYPE) {
+               if (vwrq->flags & IW_POWER_PERIOD) {
+                       vwrq->value = duration;
+                       vwrq->flags = IW_POWER_PERIOD;
+               } else {
+                       vwrq->value = duration * level / 4;
+                       vwrq->flags = IW_POWER_TIMEOUT;
+               }
+       }
+       if (vwrq->flags & IW_POWER_MODE) {
+               if (enabled && level)
+                       vwrq->flags = IW_POWER_UNICAST_R;
+               else
+                       vwrq->flags = IW_POWER_ALL_R;
+       }
+
+       return 0;
+}
+
+
+static const iw_handler zd1201_iw_handler[] =
+{
+       (iw_handler) zd1201_config_commit,      /* SIOCSIWCOMMIT */
+       (iw_handler) zd1201_get_name,           /* SIOCGIWNAME */
+       (iw_handler) NULL,                      /* SIOCSIWNWID */
+       (iw_handler) NULL,                      /* SIOCGIWNWID */
+       (iw_handler) zd1201_set_freq,           /* SIOCSIWFREQ */
+       (iw_handler) zd1201_get_freq,           /* SIOCGIWFREQ */
+       (iw_handler) zd1201_set_mode,           /* SIOCSIWMODE */
+       (iw_handler) zd1201_get_mode,           /* SIOCGIWMODE */
+       (iw_handler) NULL,                      /* SIOCSIWSENS */
+       (iw_handler) NULL,                      /* SIOCGIWSENS */
+       (iw_handler) NULL,                      /* SIOCSIWRANGE */
+       (iw_handler) zd1201_get_range,           /* SIOCGIWRANGE */
+       (iw_handler) NULL,                      /* SIOCSIWPRIV */
+       (iw_handler) NULL,                      /* SIOCGIWPRIV */
+       (iw_handler) NULL,                      /* SIOCSIWSTATS */
+       (iw_handler) NULL,                      /* SIOCGIWSTATS */
+       (iw_handler) NULL,                      /* SIOCSIWSPY */
+       (iw_handler) NULL,                      /* SIOCGIWSPY */
+       (iw_handler) NULL,                      /* -- hole -- */
+       (iw_handler) NULL,                      /* -- hole -- */
+       (iw_handler) NULL/*zd1201_set_wap*/,            /* SIOCSIWAP */
+       (iw_handler) zd1201_get_wap,            /* SIOCGIWAP */
+       (iw_handler) NULL,                      /* -- hole -- */
+       (iw_handler) NULL,                      /* SIOCGIWAPLIST */
+       (iw_handler) zd1201_set_scan,           /* SIOCSIWSCAN */
+       (iw_handler) zd1201_get_scan,           /* SIOCGIWSCAN */
+       (iw_handler) zd1201_set_essid,          /* SIOCSIWESSID */
+       (iw_handler) zd1201_get_essid,          /* SIOCGIWESSID */
+       (iw_handler) NULL,                      /* SIOCSIWNICKN */
+       (iw_handler) zd1201_get_nick,           /* SIOCGIWNICKN */
+       (iw_handler) NULL,                      /* -- hole -- */
+       (iw_handler) NULL,                      /* -- hole -- */
+       (iw_handler) zd1201_set_rate,           /* SIOCSIWRATE */
+       (iw_handler) zd1201_get_rate,           /* SIOCGIWRATE */
+       (iw_handler) zd1201_set_rts,            /* SIOCSIWRTS */
+       (iw_handler) zd1201_get_rts,            /* SIOCGIWRTS */
+       (iw_handler) zd1201_set_frag,           /* SIOCSIWFRAG */
+       (iw_handler) zd1201_get_frag,           /* SIOCGIWFRAG */
+       (iw_handler) NULL,                      /* SIOCSIWTXPOW */
+       (iw_handler) NULL,                      /* SIOCGIWTXPOW */
+       (iw_handler) zd1201_set_retry,          /* SIOCSIWRETRY */
+       (iw_handler) zd1201_get_retry,          /* SIOCGIWRETRY */
+       (iw_handler) zd1201_set_encode,         /* SIOCSIWENCODE */
+       (iw_handler) zd1201_get_encode,         /* SIOCGIWENCODE */
+       (iw_handler) zd1201_set_power,          /* SIOCSIWPOWER */
+       (iw_handler) zd1201_get_power,          /* SIOCGIWPOWER */
+};
+
+static int zd1201_set_hostauth(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       int err;
+
+       if (!zd->ap)
+               return -EOPNOTSUPP;
+
+       err = zd1201_setconfig16(zd, ZD1201_RID_CNFHOSTAUTH, rrq->value);
+       if (err)
+               return err;
+       return 0;
+}
+
+static int zd1201_get_hostauth(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       short hostauth;
+       int err;
+
+       if (!zd->ap)
+               return -EOPNOTSUPP;
+
+       err = zd1201_getconfig16(zd, ZD1201_RID_CNFHOSTAUTH, &hostauth);
+       if (err)
+               return err;
+       rrq->value = hostauth;
+       rrq->fixed = 1;
+
+       return 0;
+}
+
+static int zd1201_auth_sta(struct net_device *dev,
+    struct iw_request_info *info, struct sockaddr *sta, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       unsigned char buffer[10];
+
+       if (!zd->ap)
+               return -EOPNOTSUPP;
+
+       memcpy(buffer, sta->sa_data, ETH_ALEN);
+       *(short*)(buffer+6) = 0;        /* 0==success, 1==failure */
+       *(short*)(buffer+8) = 0;
+
+       return zd1201_setconfig(zd, ZD1201_RID_AUTHENTICATESTA, buffer, 10, 1);
+}
+
+static int zd1201_set_maxassoc(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       int err;
+
+       if (!zd->ap)
+               return -EOPNOTSUPP;
+
+       err = zd1201_setconfig16(zd, ZD1201_RID_CNFMAXASSOCSTATIONS, rrq->value);
+       if (err)
+               return err;
+       return 0;
+}
+
+static int zd1201_get_maxassoc(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+       short maxassoc;
+       int err;
+
+       if (!zd->ap)
+               return -EOPNOTSUPP;
+
+       err = zd1201_getconfig16(zd, ZD1201_RID_CNFMAXASSOCSTATIONS, &maxassoc);
+       if (err)
+               return err;
+       rrq->value = maxassoc;
+       rrq->fixed = 1;
+
+       return 0;
+}
+
+static const iw_handler zd1201_private_handler[] = {
+       (iw_handler) zd1201_set_hostauth,       /* ZD1201SIWHOSTAUTH */
+       (iw_handler) zd1201_get_hostauth,       /* ZD1201GIWHOSTAUTH */
+       (iw_handler) zd1201_auth_sta,           /* ZD1201SIWAUTHSTA */
+       (iw_handler) NULL,                      /* nothing to get */
+       (iw_handler) zd1201_set_maxassoc,       /* ZD1201SIMAXASSOC */
+       (iw_handler) zd1201_get_maxassoc,       /* ZD1201GIMAXASSOC */
+};
+
+static const struct iw_priv_args zd1201_private_args[] = {
+       { ZD1201SIWHOSTAUTH, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+           IW_PRIV_TYPE_NONE, "sethostauth" },
+       { ZD1201GIWHOSTAUTH, IW_PRIV_TYPE_NONE,
+           IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostauth" },
+       { ZD1201SIWAUTHSTA, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 
+           IW_PRIV_TYPE_NONE, "authstation" },
+       { ZD1201SIWMAXASSOC, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+           IW_PRIV_TYPE_NONE, "setmaxassoc" },
+       { ZD1201GIWMAXASSOC, IW_PRIV_TYPE_NONE,
+           IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmaxassoc" },
+};
+
+static const struct iw_handler_def zd1201_iw_handlers = {
+       .num_standard           = sizeof(zd1201_iw_handler)/sizeof(iw_handler),
+       .num_private            = sizeof(zd1201_private_handler)/sizeof(iw_handler),
+       .num_private_args       = sizeof(zd1201_private_args)/sizeof(struct iw_priv_args),
+       .standard               = (iw_handler *)zd1201_iw_handler,
+       .private                = (iw_handler *)zd1201_private_handler,
+       .private_args           = (struct iw_priv_args *) zd1201_private_args,
+};
+
+static int zd1201_probe(struct usb_interface *interface,
+                       const struct usb_device_id *id)
+{
+       struct zd1201 *zd;
+       struct usb_device *usb;
+       int i, err;
+       short porttype;
+       char buf[IW_ESSID_MAX_SIZE+2];
+
+       usb = interface_to_usbdev(interface);
+
+       zd = kmalloc(sizeof(struct zd1201), GFP_KERNEL);
+       if (!zd) {
+               return -ENOMEM;
+       }
+       memset(zd, 0, sizeof(struct zd1201));
+       zd->ap = ap;
+       zd->usb = usb;
+       zd->removed = 0;
+       init_waitqueue_head(&zd->rxdataq);
+       INIT_HLIST_HEAD(&zd->fraglist);
+       
+       err = zd1201_fw_upload(usb, zd->ap);
+       if (err) {
+               dev_err(&usb->dev, "zd1201 firmware upload failed: %d\n", err);
+               goto err_zd;
+       }
+       
+       zd->endp_in = 1;
+       zd->endp_out = 1;
+       zd->endp_out2 = 2;
+       zd->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+       zd->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!zd->rx_urb || !zd->tx_urb)
+               goto err_zd;
+
+       for(i = 0; i<100; i++)
+               udelay(1000);
+
+       err = zd1201_drvr_start(zd);
+       if (err)
+               goto err_zd;
+
+       err = zd1201_setconfig16(zd, ZD1201_RID_CNFMAXDATALEN, 2312);
+       if (err)
+               goto err_start;
+
+       err = zd1201_setconfig16(zd, ZD1201_RID_TXRATECNTL,
+           ZD1201_RATEB1 | ZD1201_RATEB2 | ZD1201_RATEB5 | ZD1201_RATEB11);
+       if (err)
+               goto err_start;
+
+       zd->dev = alloc_etherdev(0);
+       if (!zd->dev)
+               goto err_start;
+
+       zd->dev->priv = zd;
+       zd->dev->open = zd1201_net_open;
+       zd->dev->stop = zd1201_net_stop;
+       zd->dev->get_stats = zd1201_get_stats;
+       zd->dev->get_wireless_stats = zd1201_get_wireless_stats;
+       zd->dev->wireless_handlers =
+           (struct iw_handler_def *)&zd1201_iw_handlers;
+       zd->dev->hard_start_xmit = zd1201_hard_start_xmit;
+       zd->dev->watchdog_timeo = ZD1201_TX_TIMEOUT;
+       zd->dev->tx_timeout = zd1201_tx_timeout;
+       zd->dev->set_multicast_list = zd1201_set_multicast;
+       zd->dev->set_mac_address = zd1201_set_mac_address;
+       strcpy(zd->dev->name, "wlan%d");
+
+       err = zd1201_getconfig(zd, ZD1201_RID_CNFOWNMACADDR, 
+           zd->dev->dev_addr, zd->dev->addr_len);
+       if (err)
+               goto err_net;
+
+       /* Set wildcard essid to match zd->essid */
+       *(__le16 *)buf = cpu_to_le16(0);
+       err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf,
+           IW_ESSID_MAX_SIZE+2, 1);
+       if (err)
+               goto err_net;
+
+       if (zd->ap)
+               porttype = ZD1201_PORTTYPE_AP;
+       else
+               porttype = ZD1201_PORTTYPE_BSS;
+       err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype);
+       if (err)
+               goto err_net;
+
+       err = register_netdev(zd->dev);
+       if (err)
+               goto err_net;
+       dev_info(&usb->dev, "%s: ZD1201 USB Wireless interface\n",
+           zd->dev->name);
+       
+       usb_set_intfdata(interface, zd);
+       return 0;
+
+err_net:
+       free_netdev(zd->dev);
+err_start:
+       /* Leave the device in reset state */
+       zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0);
+err_zd:
+       if (zd->tx_urb)
+               usb_free_urb(zd->tx_urb);
+       if (zd->rx_urb)
+               usb_free_urb(zd->rx_urb);
+       kfree(zd);
+       return err;
+}
+
+static void zd1201_disconnect(struct usb_interface *interface)
+{
+       struct zd1201 *zd=(struct zd1201 *)usb_get_intfdata(interface);
+       struct hlist_node *node, *node2;
+       struct zd1201_frag *frag;
+
+       if (!zd)
+               return;
+       usb_set_intfdata(interface, NULL);
+       if (zd->dev) {
+               unregister_netdev(zd->dev);
+               free_netdev(zd->dev);
+       }
+
+       hlist_for_each_entry_safe(frag, node, node2, &zd->fraglist, fnode) {
+               hlist_del_init(&frag->fnode);
+               kfree_skb(frag->skb);
+               kfree(frag);
+       }
+
+       if (zd->tx_urb) {
+               usb_kill_urb(zd->tx_urb);
+               usb_free_urb(zd->tx_urb);
+       }
+       if (zd->rx_urb) {
+               usb_kill_urb(zd->rx_urb);
+               usb_free_urb(zd->rx_urb);
+       }
+       kfree(zd);
+}
+
+static struct usb_driver zd1201_usb = {
+       .owner = THIS_MODULE,
+       .name = "zd1201",
+       .probe = zd1201_probe,
+       .disconnect = zd1201_disconnect,
+       .id_table = zd1201_table,
+};
+
+static int __init zd1201_init(void)
+{
+       return usb_register(&zd1201_usb);
+}
+
+static void __exit zd1201_cleanup(void)
+{
+       usb_deregister(&zd1201_usb);
+}
+
+module_init(zd1201_init);
+module_exit(zd1201_cleanup);
diff --git a/drivers/usb/net/zd1201.h b/drivers/usb/net/zd1201.h
new file mode 100644 (file)
index 0000000..1627c71
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ *     Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org)
+ *
+ *     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.
+ *
+ *     Parts of this driver have been derived from a wlan-ng version
+ *     modified by ZyDAS.
+ *     Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
+ */
+
+#ifndef _INCLUDE_ZD1201_H_
+#define _INCLUDE_ZD1201_H_
+
+#define ZD1201_NUMKEYS         4
+#define ZD1201_MAXKEYLEN       13
+#define ZD1201_MAXMULTI                16
+#define ZD1201_FRAGMAX         2500
+#define ZD1201_FRAGMIN         256
+#define ZD1201_RTSMAX          2500
+
+#define ZD1201_RXSIZE          3000
+
+struct zd1201 {
+       struct usb_device       *usb;
+       int                     removed;
+       struct net_device       *dev;
+       struct net_device_stats stats;
+       struct iw_statistics    iwstats;
+
+       int                     endp_in;
+       int                     endp_out;
+       int                     endp_out2;
+       struct urb              *rx_urb;
+       struct urb              *tx_urb;
+
+       unsigned char           rxdata[ZD1201_RXSIZE];
+       int                     rxlen;
+       wait_queue_head_t       rxdataq;
+       int                     rxdatas;
+       struct hlist_head       fraglist;
+       unsigned char           txdata[ZD1201_RXSIZE];
+
+       int                     ap;
+       char                    essid[IW_ESSID_MAX_SIZE+1];
+       int                     essidlen;
+       int                     mac_enabled;
+       int                     monitor;
+       int                     encode_enabled;
+       int                     encode_restricted;
+       unsigned char           encode_keys[ZD1201_NUMKEYS][ZD1201_MAXKEYLEN];
+       int                     encode_keylen[ZD1201_NUMKEYS];
+};
+
+struct zd1201_frag {
+       struct hlist_node       fnode;
+       int                     seq;
+       struct sk_buff          *skb;
+};
+
+#define ZD1201SIWHOSTAUTH SIOCIWFIRSTPRIV
+#define ZD1201GIWHOSTAUTH ZD1201SIWHOSTAUTH+1
+#define ZD1201SIWAUTHSTA SIOCIWFIRSTPRIV+2
+#define ZD1201SIWMAXASSOC SIOCIWFIRSTPRIV+4
+#define ZD1201GIWMAXASSOC ZD1201SIWMAXASSOC+1
+
+#define ZD1201_FW_TIMEOUT      (1000)
+
+#define ZD1201_TX_TIMEOUT      (2000)
+
+#define ZD1201_USB_CMDREQ      0
+#define ZD1201_USB_RESREQ      1
+
+#define        ZD1201_CMDCODE_INIT     0x00
+#define ZD1201_CMDCODE_ENABLE  0x01
+#define ZD1201_CMDCODE_DISABLE 0x02
+#define ZD1201_CMDCODE_ALLOC   0x0a
+#define ZD1201_CMDCODE_INQUIRE 0x11
+#define ZD1201_CMDCODE_SETRXRID        0x17
+#define ZD1201_CMDCODE_ACCESS  0x21
+
+#define ZD1201_PACKET_EVENTSTAT        0x0
+#define ZD1201_PACKET_RXDATA   0x1
+#define ZD1201_PACKET_INQUIRE  0x2
+#define ZD1201_PACKET_RESOURCE 0x3
+
+#define ZD1201_ACCESSBIT       0x0100
+
+#define ZD1201_RID_CNFPORTTYPE         0xfc00
+#define ZD1201_RID_CNFOWNMACADDR       0xfc01
+#define ZD1201_RID_CNFDESIREDSSID      0xfc02
+#define ZD1201_RID_CNFOWNCHANNEL       0xfc03
+#define ZD1201_RID_CNFOWNSSID          0xfc04
+#define ZD1201_RID_CNFMAXDATALEN       0xfc07
+#define ZD1201_RID_CNFPMENABLED                0xfc09
+#define ZD1201_RID_CNFPMEPS            0xfc0a
+#define ZD1201_RID_CNFMAXSLEEPDURATION 0xfc0c
+#define ZD1201_RID_CNFDEFAULTKEYID     0xfc23
+#define ZD1201_RID_CNFDEFAULTKEY0      0xfc24
+#define ZD1201_RID_CNFDEFAULTKEY1      0xfc25
+#define ZD1201_RID_CNFDEFAULTKEY2      0xfc26
+#define ZD1201_RID_CNFDEFAULTKEY3      0xfc27
+#define ZD1201_RID_CNFWEBFLAGS         0xfc28
+#define ZD1201_RID_CNFAUTHENTICATION   0xfc2a
+#define ZD1201_RID_CNFMAXASSOCSTATIONS 0xfc2b
+#define ZD1201_RID_CNFHOSTAUTH         0xfc2e
+#define ZD1201_RID_CNFGROUPADDRESS     0xfc80
+#define ZD1201_RID_CNFFRAGTHRESHOLD    0xfc82
+#define ZD1201_RID_CNFRTSTHRESHOLD     0xfc83
+#define ZD1201_RID_TXRATECNTL          0xfc84
+#define ZD1201_RID_PROMISCUOUSMODE     0xfc85
+#define ZD1201_RID_CNFBASICRATES       0xfcb3
+#define ZD1201_RID_AUTHENTICATESTA     0xfce3
+#define ZD1201_RID_CURRENTBSSID                0xfd42
+#define ZD1201_RID_COMMSQUALITY                0xfd43
+#define ZD1201_RID_CURRENTTXRATE       0xfd44
+#define ZD1201_RID_CNFMAXTXBUFFERNUMBER        0xfda0
+#define ZD1201_RID_CURRENTCHANNEL      0xfdc1
+
+#define ZD1201_INQ_SCANRESULTS         0xf101
+
+#define ZD1201_INF_LINKSTATUS          0xf200
+#define ZD1201_INF_ASSOCSTATUS         0xf201
+#define ZD1201_INF_AUTHREQ             0xf202
+
+#define ZD1201_ASSOCSTATUS_STAASSOC    0x1
+#define ZD1201_ASSOCSTATUS_REASSOC     0x2
+#define ZD1201_ASSOCSTATUS_DISASSOC    0x3
+#define ZD1201_ASSOCSTATUS_ASSOCFAIL   0x4
+#define ZD1201_ASSOCSTATUS_AUTHFAIL    0x5
+
+#define ZD1201_PORTTYPE_IBSS           0
+#define ZD1201_PORTTYPE_BSS            1
+#define ZD1201_PORTTYPE_WDS            2
+#define ZD1201_PORTTYPE_PSEUDOIBSS     3
+#define ZD1201_PORTTYPE_AP             6
+
+#define ZD1201_RATEB1  1
+#define ZD1201_RATEB2  2
+#define ZD1201_RATEB5  4       /* 5.5 really, but 5 is shorter :) */
+#define ZD1201_RATEB11 8
+
+#define ZD1201_CNFAUTHENTICATION_OPENSYSTEM    0x0001
+#define ZD1201_CNFAUTHENTICATION_SHAREDKEY     0x0002
+
+#endif /* _INCLUDE_ZD1201_H_ */
diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c
new file mode 100644 (file)
index 0000000..a4ce000
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * AirPrime CDMA Wireless Serial USB driver
+ *
+ * Copyright (C) 2005 Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ *     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/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include "usb-serial.h"
+
+static struct usb_device_id id_table [] = {
+       { USB_DEVICE(0xf3d, 0x0112) },
+       { },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver airprime_driver = {
+       .owner =        THIS_MODULE,
+       .name =         "airprime",
+       .probe =        usb_serial_probe,
+       .disconnect =   usb_serial_disconnect,
+       .id_table =     id_table,
+};
+
+static struct usb_serial_device_type airprime_device = {
+       .owner =                THIS_MODULE,
+       .name =                 "airprime",
+       .id_table =             id_table,
+       .num_interrupt_in =     NUM_DONT_CARE,
+       .num_bulk_in =          NUM_DONT_CARE,
+       .num_bulk_out =         NUM_DONT_CARE,
+       .num_ports =            1,
+};
+
+static int __init airprime_init(void)
+{
+       int retval;
+
+       retval = usb_serial_register(&airprime_device);
+       if (retval)
+               return retval;
+       retval = usb_register(&airprime_driver);
+       if (retval)
+               usb_serial_deregister(&airprime_device);
+       return retval;
+}
+
+static void __exit airprime_exit(void)
+{
+       usb_deregister(&airprime_driver);
+       usb_serial_deregister(&airprime_device);
+}
+
+module_init(airprime_init);
+module_exit(airprime_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c
new file mode 100644 (file)
index 0000000..4ace996
--- /dev/null
@@ -0,0 +1,778 @@
+/*
+ * Silicon Laboratories CP2101/CP2102 USB to RS232 serial adaptor driver
+ *
+ * Copyright (C) 2005 Craig Shelley (craig@microtron.org.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.
+ *
+ * Support to set flow control line levels using TIOCMGET and TIOCMSET
+ * thanks to Karl Hiramoto karl@hiramoto.org. RTSCTS hardware flow
+ * control thanks to Munir Nassar nassarmu@real-time.com
+ *
+ * Outstanding Issues:
+ *  Buffers are not flushed when the port is opened.
+ *  Multiple calls to write() may fail with "Resource temporarily unavailable"
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+#include "usb-serial.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.04"
+#define DRIVER_DESC "Silicon Labs CP2101/CP2102 RS232 serial adaptor driver"
+
+/*
+ * Function Prototypes
+ */
+static int cp2101_open(struct usb_serial_port*, struct file*);
+static void cp2101_cleanup(struct usb_serial_port*);
+static void cp2101_close(struct usb_serial_port*, struct file*);
+static void cp2101_get_termios(struct usb_serial_port*);
+static void cp2101_set_termios(struct usb_serial_port*, struct termios*);
+static int cp2101_tiocmget (struct usb_serial_port *, struct file *);
+static int cp2101_tiocmset (struct usb_serial_port *, struct file *,
+               unsigned int, unsigned int);
+static void cp2101_break_ctl(struct usb_serial_port*, int);
+static int cp2101_startup (struct usb_serial *);
+static void cp2101_shutdown(struct usb_serial*);
+
+
+static int debug;
+
+static struct usb_device_id id_table [] = {
+       { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
+       { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
+       { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
+       { } /* Terminating Entry */
+};
+
+MODULE_DEVICE_TABLE (usb, id_table);
+
+static struct usb_driver cp2101_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "CP2101",
+       .probe          = usb_serial_probe,
+       .disconnect     = usb_serial_disconnect,
+       .id_table       = id_table,
+};
+
+static struct usb_serial_device_type cp2101_device = {
+       .owner                  = THIS_MODULE,
+       .name                   = "CP2101",
+       .id_table               = id_table,
+       .num_interrupt_in       = 0,
+       .num_bulk_in            = 0,
+       .num_bulk_out           = 0,
+       .num_ports              = 1,
+       .open                   = cp2101_open,
+       .close                  = cp2101_close,
+       .break_ctl              = cp2101_break_ctl,
+       .set_termios            = cp2101_set_termios,
+       .tiocmget               = cp2101_tiocmget,
+       .tiocmset               = cp2101_tiocmset,
+       .attach                 = cp2101_startup,
+       .shutdown               = cp2101_shutdown,
+};
+
+/* Config request types */
+#define REQTYPE_HOST_TO_DEVICE 0x41
+#define REQTYPE_DEVICE_TO_HOST 0xc1
+
+/* Config SET requests. To GET, add 1 to the request number */
+#define CP2101_UART            0x00    /* Enable / Disable */
+#define CP2101_BAUDRATE                0x01    /* (BAUD_RATE_GEN_FREQ / baudrate) */
+#define CP2101_BITS            0x03    /* 0x(0)(databits)(parity)(stopbits) */
+#define CP2101_BREAK           0x05    /* On / Off */
+#define CP2101_CONTROL         0x07    /* Flow control line states */
+#define CP2101_MODEMCTL                0x13    /* Modem controls */
+#define CP2101_CONFIG_6                0x19    /* 6 bytes of config data ??? */
+
+/* CP2101_UART */
+#define UART_ENABLE            0x0001
+#define UART_DISABLE           0x0000
+
+/* CP2101_BAUDRATE */
+#define BAUD_RATE_GEN_FREQ     0x384000
+
+/* CP2101_BITS */
+#define BITS_DATA_MASK         0X0f00
+#define BITS_DATA_5            0X0500
+#define BITS_DATA_6            0X0600
+#define BITS_DATA_7            0X0700
+#define BITS_DATA_8            0X0800
+#define BITS_DATA_9            0X0900
+
+#define BITS_PARITY_MASK       0x00f0
+#define BITS_PARITY_NONE       0x0000
+#define BITS_PARITY_ODD                0x0010
+#define BITS_PARITY_EVEN       0x0020
+#define BITS_PARITY_MARK       0x0030
+#define BITS_PARITY_SPACE      0x0040
+
+#define BITS_STOP_MASK         0x000f
+#define BITS_STOP_1            0x0000
+#define BITS_STOP_1_5          0x0001
+#define BITS_STOP_2            0x0002
+
+/* CP2101_BREAK */
+#define BREAK_ON               0x0000
+#define BREAK_OFF              0x0001
+
+/* CP2101_CONTROL */
+#define CONTROL_DTR            0x0001
+#define CONTROL_RTS            0x0002
+#define CONTROL_CTS            0x0010
+#define CONTROL_DSR            0x0020
+#define CONTROL_RING           0x0040
+#define CONTROL_DCD            0x0080
+#define CONTROL_WRITE_DTR      0x0100
+#define CONTROL_WRITE_RTS      0x0200
+
+/*
+ * cp2101_get_config
+ * Reads from the CP2101 configuration registers
+ * 'size' is specified in bytes.
+ * 'data' is a pointer to a pre-allocated array of integers large
+ * enough to hold 'size' bytes (with 4 bytes to each integer)
+ */
+static int cp2101_get_config(struct usb_serial_port* port, u8 request,
+               unsigned int *data, int size)
+{
+       struct usb_serial *serial = port->serial;
+       u32 *buf;
+       int result, i, length;
+
+       /* Number of integers required to contain the array */
+       length = (((size - 1) | 3) + 1)/4;
+
+       buf = kmalloc (length * sizeof(u32), GFP_KERNEL);
+       memset(buf, 0, length * sizeof(u32));
+
+       if (!buf) {
+               dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__);
+               return -ENOMEM;
+       }
+
+       /* For get requests, the request number must be incremented */
+       request++;
+
+       /* Issue the request, attempting to read 'size' bytes */
+       result = usb_control_msg (serial->dev,usb_rcvctrlpipe (serial->dev, 0),
+                               request, REQTYPE_DEVICE_TO_HOST, 0x0000,
+                               0, buf, size, 300);
+
+       /* Convert data into an array of integers */
+       for (i=0; i<length; i++)
+               data[i] = le32_to_cpu(buf[i]);
+
+       kfree(buf);
+
+       if (result != size) {
+               dev_err(&port->dev, "%s - Unable to send config request, "
+                               "request=0x%x size=%d result=%d\n",
+                               __FUNCTION__, request, size, result);
+               return -EPROTO;
+       }
+
+       return 0;
+}
+
+/*
+ * cp2101_set_config
+ * Writes to the CP2101 configuration registers
+ * Values less than 16 bits wide are sent directly
+ * 'size' is specified in bytes.
+ */
+static int cp2101_set_config(struct usb_serial_port* port, u8 request,
+               unsigned int *data, int size)
+{
+       struct usb_serial *serial = port->serial;
+       u32 *buf;
+       int result, i, length;
+
+       /* Number of integers required to contain the array */
+       length = (((size - 1) | 3) + 1)/4;
+
+       buf = kmalloc(length * sizeof(u32), GFP_KERNEL);
+       if (!buf) {
+               dev_err(&port->dev, "%s - out of memory.\n",
+                               __FUNCTION__);
+               return -ENOMEM;
+       }
+
+       /* Array of integers into bytes */
+       for (i = 0; i < length; i++)
+               buf[i] = cpu_to_le32(data[i]);
+
+       if (size > 2) {
+               result = usb_control_msg (serial->dev,
+                               usb_sndctrlpipe(serial->dev, 0),
+                               request, REQTYPE_HOST_TO_DEVICE, 0x0000,
+                               0, buf, size, 300);
+       } else {
+               result = usb_control_msg (serial->dev,
+                               usb_sndctrlpipe(serial->dev, 0),
+                               request, REQTYPE_HOST_TO_DEVICE, data[0],
+                               0, NULL, 0, 300);
+       }
+
+       kfree(buf);
+
+       if ((size > 2 && result != size) || result < 0) {
+               dev_err(&port->dev, "%s - Unable to send request, "
+                               "request=0x%x size=%d result=%d\n",
+                               __FUNCTION__, request, size, result);
+               return -EPROTO;
+       }
+
+       /* Single data value */
+       result = usb_control_msg (serial->dev,
+                       usb_sndctrlpipe(serial->dev, 0),
+                       request, REQTYPE_HOST_TO_DEVICE, data[0],
+                       0, NULL, 0, 300);
+       return 0;
+}
+
+/*
+ * cp2101_set_config_single
+ * Convenience function for calling cp2101_set_config on single data values
+ * without requiring an integer pointer
+ */
+static inline int cp2101_set_config_single(struct usb_serial_port* port,
+               u8 request, unsigned int data)
+{
+       return cp2101_set_config(port, request, &data, 2);
+}
+
+static int cp2101_open (struct usb_serial_port *port, struct file *filp)
+{
+       struct usb_serial *serial = port->serial;
+       int result;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (cp2101_set_config_single(port, CP2101_UART, UART_ENABLE)) {
+               dev_err(&port->dev, "%s - Unable to enable UART\n",
+                               __FUNCTION__);
+               return -EPROTO;
+       }
+
+       /* Start reading from the device */
+       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,
+                       serial->type->read_bulk_callback,
+                       port);
+       result = usb_submit_urb(port->read_urb, GFP_KERNEL);
+       if (result) {
+               dev_err(&port->dev, "%s - failed resubmitting read urb, "
+                               "error %d\n", __FUNCTION__, result);
+               return result;
+       }
+
+       /* Configure the termios structure */
+       cp2101_get_termios(port);
+
+       /* Set the DTR and RTS pins low */
+       cp2101_tiocmset(port, NULL, TIOCM_DTR | TIOCM_RTS, 0);
+
+       return 0;
+}
+
+static void cp2101_cleanup (struct usb_serial_port *port)
+{
+       struct usb_serial *serial = port->serial;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (serial->dev) {
+               /* shutdown any bulk reads that might be going on */
+               if (serial->num_bulk_out)
+                       usb_kill_urb(port->write_urb);
+               if (serial->num_bulk_in)
+                       usb_kill_urb(port->read_urb);
+       }
+}
+
+static void cp2101_close (struct usb_serial_port *port, struct file * filp)
+{
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       /* shutdown our urbs */
+       dbg("%s - shutting down urbs", __FUNCTION__);
+       usb_kill_urb(port->write_urb);
+       usb_kill_urb(port->read_urb);
+
+       cp2101_set_config_single(port, CP2101_UART, UART_DISABLE);
+}
+
+/*
+ * cp2101_get_termios
+ * Reads the baud rate, data bits, parity, stop bits and flow control mode
+ * from the device, corrects any unsupported values, and configures the
+ * termios structure to reflect the state of the device
+ */
+static void cp2101_get_termios (struct usb_serial_port *port)
+{
+       unsigned int cflag, modem_ctl[4];
+       int baud;
+       int bits;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if ((!port->tty) || (!port->tty->termios)) {
+               dbg("%s - no tty structures", __FUNCTION__);
+               return;
+       }
+       cflag = port->tty->termios->c_cflag;
+
+       cp2101_get_config(port, CP2101_BAUDRATE, &baud, 2);
+       /* Convert to baudrate */
+       if (baud)
+               baud = BAUD_RATE_GEN_FREQ / baud;
+
+       dbg("%s - baud rate = %d", __FUNCTION__, baud);
+       cflag &= ~CBAUD;
+       switch (baud) {
+               /*
+                * The baud rates which are commented out below
+                * appear to be supported by the device
+                * but are non-standard
+                */
+               case 600:       cflag |= B600;          break;
+               case 1200:      cflag |= B1200;         break;
+               case 1800:      cflag |= B1800;         break;
+               case 2400:      cflag |= B2400;         break;
+               case 4800:      cflag |= B4800;         break;
+               /*case 7200:    cflag |= B7200;         break;*/
+               case 9600:      cflag |= B9600;         break;
+               /*case 14400:   cflag |= B14400;        break;*/
+               case 19200:     cflag |= B19200;        break;
+               /*case 28800:   cflag |= B28800;        break;*/
+               case 38400:     cflag |= B38400;        break;
+               /*case 55854:   cflag |= B55054;        break;*/
+               case 57600:     cflag |= B57600;        break;
+               case 115200:    cflag |= B115200;       break;
+               /*case 127117:  cflag |= B127117;       break;*/
+               case 230400:    cflag |= B230400;       break;
+               case 460800:    cflag |= B460800;       break;
+               case 921600:    cflag |= B921600;       break;
+               /*case 3686400: cflag |= B3686400;      break;*/
+               default:
+                       dbg("%s - Baud rate is not supported, "
+                                       "using 9600 baud", __FUNCTION__);
+                       cflag |= B9600;
+                       cp2101_set_config_single(port, CP2101_BAUDRATE,
+                                       (BAUD_RATE_GEN_FREQ/9600));
+                       break;
+       }
+
+       cp2101_get_config(port, CP2101_BITS, &bits, 2);
+       cflag &= ~CSIZE;
+       switch(bits & BITS_DATA_MASK) {
+               case BITS_DATA_5:
+                       dbg("%s - data bits = 5", __FUNCTION__);
+                       cflag |= CS5;
+                       break;
+               case BITS_DATA_6:
+                       dbg("%s - data bits = 6", __FUNCTION__);
+                       cflag |= CS6;
+                       break;
+               case BITS_DATA_7:
+                       dbg("%s - data bits = 7", __FUNCTION__);
+                       cflag |= CS7;
+                       break;
+               case BITS_DATA_8:
+                       dbg("%s - data bits = 8", __FUNCTION__);
+                       cflag |= CS8;
+                       break;
+               case BITS_DATA_9:
+                       dbg("%s - data bits = 9 (not supported, "
+                                       "using 8 data bits)", __FUNCTION__);
+                       cflag |= CS8;
+                       bits &= ~BITS_DATA_MASK;
+                       bits |= BITS_DATA_8;
+                       cp2101_set_config(port, CP2101_BITS, &bits, 2);
+                       break;
+               default:
+                       dbg("%s - Unknown number of data bits, "
+                                       "using 8", __FUNCTION__);
+                       cflag |= CS8;
+                       bits &= ~BITS_DATA_MASK;
+                       bits |= BITS_DATA_8;
+                       cp2101_set_config(port, CP2101_BITS, &bits, 2);
+                       break;
+       }
+
+       switch(bits & BITS_PARITY_MASK) {
+               case BITS_PARITY_NONE:
+                       dbg("%s - parity = NONE", __FUNCTION__);
+                       cflag &= ~PARENB;
+                       break;
+               case BITS_PARITY_ODD:
+                       dbg("%s - parity = ODD", __FUNCTION__);
+                       cflag |= (PARENB|PARODD);
+                       break;
+               case BITS_PARITY_EVEN:
+                       dbg("%s - parity = EVEN", __FUNCTION__);
+                       cflag &= ~PARODD;
+                       cflag |= PARENB;
+                       break;
+               case BITS_PARITY_MARK:
+                       dbg("%s - parity = MARK (not supported, "
+                                       "disabling parity)", __FUNCTION__);
+                       cflag &= ~PARENB;
+                       bits &= ~BITS_PARITY_MASK;
+                       cp2101_set_config(port, CP2101_BITS, &bits, 2);
+                       break;
+               case BITS_PARITY_SPACE:
+                       dbg("%s - parity = SPACE (not supported, "
+                                       "disabling parity)", __FUNCTION__);
+                       cflag &= ~PARENB;
+                       bits &= ~BITS_PARITY_MASK;
+                       cp2101_set_config(port, CP2101_BITS, &bits, 2);
+                       break;
+               default:
+                       dbg("%s - Unknown parity mode, "
+                                       "disabling parity", __FUNCTION__);
+                       cflag &= ~PARENB;
+                       bits &= ~BITS_PARITY_MASK;
+                       cp2101_set_config(port, CP2101_BITS, &bits, 2);
+                       break;
+       }
+
+       cflag &= ~CSTOPB;
+       switch(bits & BITS_STOP_MASK) {
+               case BITS_STOP_1:
+                       dbg("%s - stop bits = 1", __FUNCTION__);
+                       break;
+               case BITS_STOP_1_5:
+                       dbg("%s - stop bits = 1.5 (not supported, "
+                                       "using 1 stop bit)", __FUNCTION__);
+                       bits &= ~BITS_STOP_MASK;
+                       cp2101_set_config(port, CP2101_BITS, &bits, 2);
+                       break;
+               case BITS_STOP_2:
+                       dbg("%s - stop bits = 2", __FUNCTION__);
+                       cflag |= CSTOPB;
+                       break;
+               default:
+                       dbg("%s - Unknown number of stop bits, "
+                                       "using 1 stop bit", __FUNCTION__);
+                       bits &= ~BITS_STOP_MASK;
+                       cp2101_set_config(port, CP2101_BITS, &bits, 2);
+                       break;
+       }
+
+       cp2101_get_config(port, CP2101_MODEMCTL, modem_ctl, 16);
+       if (modem_ctl[0] & 0x0008) {
+               dbg("%s - flow control = CRTSCTS", __FUNCTION__);
+               cflag |= CRTSCTS;
+       } else {
+               dbg("%s - flow control = NONE", __FUNCTION__);
+               cflag &= ~CRTSCTS;
+       }
+
+       port->tty->termios->c_cflag = cflag;
+}
+
+static void cp2101_set_termios (struct usb_serial_port *port,
+               struct termios *old_termios)
+{
+       unsigned int cflag, old_cflag=0;
+       int baud=0, bits;
+       unsigned int modem_ctl[4];
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if ((!port->tty) || (!port->tty->termios)) {
+               dbg("%s - no tty structures", __FUNCTION__);
+               return;
+       }
+       cflag = port->tty->termios->c_cflag;
+
+       /* Check that they really want us to change something */
+       if (old_termios) {
+               if ((cflag == old_termios->c_cflag) &&
+                               (RELEVANT_IFLAG(port->tty->termios->c_iflag)
+                               == RELEVANT_IFLAG(old_termios->c_iflag))) {
+                       dbg("%s - nothing to change...", __FUNCTION__);
+                       return;
+               }
+
+               old_cflag = old_termios->c_cflag;
+       }
+
+       /* If the baud rate is to be updated*/
+       if ((cflag & CBAUD) != (old_cflag & CBAUD)) {
+               switch (cflag & CBAUD) {
+                       /*
+                        * The baud rates which are commented out below
+                        * appear to be supported by the device
+                        * but are non-standard
+                        */
+                       case B0:        baud = 0;       break;
+                       case B600:      baud = 600;     break;
+                       case B1200:     baud = 1200;    break;
+                       case B1800:     baud = 1800;    break;
+                       case B2400:     baud = 2400;    break;
+                       case B4800:     baud = 4800;    break;
+                       /*case B7200:   baud = 7200;    break;*/
+                       case B9600:     baud = 9600;    break;
+                       /*ase B14400:   baud = 14400;   break;*/
+                       case B19200:    baud = 19200;   break;
+                       /*case B28800:  baud = 28800;   break;*/
+                       case B38400:    baud = 38400;   break;
+                       /*case B55854:  baud = 55054;   break;*/
+                       case B57600:    baud = 57600;   break;
+                       case B115200:   baud = 115200;  break;
+                       /*case B127117: baud = 127117;  break;*/
+                       case B230400:   baud = 230400;  break;
+                       case B460800:   baud = 460800;  break;
+                       case B921600:   baud = 921600;  break;
+                       /*case B3686400:        baud = 3686400; break;*/
+                       default:
+                               dev_err(&port->dev, "cp2101 driver does not "
+                                       "support the baudrate requested\n");
+                               break;
+               }
+
+               if (baud) {
+                       dbg("%s - Setting baud rate to %d baud", __FUNCTION__,
+                                       baud);
+                       if (cp2101_set_config_single(port, CP2101_BAUDRATE,
+                                               (BAUD_RATE_GEN_FREQ / baud)))
+                               dev_err(&port->dev, "Baud rate requested not "
+                                               "supported by device\n");
+               }
+       }
+
+       /* If the number of data bits is to be updated */
+       if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
+               cp2101_get_config(port, CP2101_BITS, &bits, 2);
+               bits &= ~BITS_DATA_MASK;
+               switch (cflag & CSIZE) {
+                       case CS5:
+                               bits |= BITS_DATA_5;
+                               dbg("%s - data bits = 5", __FUNCTION__);
+                               break;
+                       case CS6:
+                               bits |= BITS_DATA_6;
+                               dbg("%s - data bits = 6", __FUNCTION__);
+                               break;
+                       case CS7:
+                               bits |= BITS_DATA_7;
+                               dbg("%s - data bits = 7", __FUNCTION__);
+                               break;
+                       case CS8:
+                               bits |= BITS_DATA_8;
+                               dbg("%s - data bits = 8", __FUNCTION__);
+                               break;
+                       /*case CS9:
+                               bits |= BITS_DATA_9;
+                               dbg("%s - data bits = 9", __FUNCTION__);
+                               break;*/
+                       default:
+                               dev_err(&port->dev, "cp2101 driver does not "
+                                       "support the number of bits requested,"
+                                       " using 8 bit mode\n");
+                               bits |= BITS_DATA_8;
+                               break;
+               }
+               if (cp2101_set_config(port, CP2101_BITS, &bits, 2))
+                       dev_err(&port->dev, "Number of data bits requested "
+                                       "not supported by device\n");
+       }
+
+       if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))) {
+               cp2101_get_config(port, CP2101_BITS, &bits, 2);
+               bits &= ~BITS_PARITY_MASK;
+               if (cflag & PARENB) {
+                       if (cflag & PARODD) {
+                               bits |= BITS_PARITY_ODD;
+                               dbg("%s - parity = ODD", __FUNCTION__);
+                       } else {
+                               bits |= BITS_PARITY_EVEN;
+                               dbg("%s - parity = EVEN", __FUNCTION__);
+                       }
+               }
+               if (cp2101_set_config(port, CP2101_BITS, &bits, 2))
+                       dev_err(&port->dev, "Parity mode not supported "
+                                       "by device\n");
+       }
+
+       if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
+               cp2101_get_config(port, CP2101_BITS, &bits, 2);
+               bits &= ~BITS_STOP_MASK;
+               if (cflag & CSTOPB) {
+                       bits |= BITS_STOP_2;
+                       dbg("%s - stop bits = 2", __FUNCTION__);
+               } else {
+                       bits |= BITS_STOP_1;
+                       dbg("%s - stop bits = 1", __FUNCTION__);
+               }
+               if (cp2101_set_config(port, CP2101_BITS, &bits, 2))
+                       dev_err(&port->dev, "Number of stop bits requested "
+                                       "not supported by device\n");
+       }
+
+       if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
+               cp2101_get_config(port, CP2101_MODEMCTL, modem_ctl, 16);
+               dbg("%s - read modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x",
+                               __FUNCTION__, modem_ctl[0], modem_ctl[1],
+                               modem_ctl[2], modem_ctl[3]);
+
+               if (cflag & CRTSCTS) {
+                       modem_ctl[0] &= ~0x7B;
+                       modem_ctl[0] |= 0x09;
+                       modem_ctl[1] = 0x80;
+                       dbg("%s - flow control = CRTSCTS", __FUNCTION__);
+               } else {
+                       modem_ctl[0] &= ~0x7B;
+                       modem_ctl[0] |= 0x01;
+                       modem_ctl[1] |= 0x40;
+                       dbg("%s - flow control = NONE", __FUNCTION__);
+               }
+
+               dbg("%s - write modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x",
+                               __FUNCTION__, modem_ctl[0], modem_ctl[1],
+                               modem_ctl[2], modem_ctl[3]);
+               cp2101_set_config(port, CP2101_MODEMCTL, modem_ctl, 16);
+       }
+
+}
+
+static int cp2101_tiocmset (struct usb_serial_port *port, struct file *file,
+               unsigned int set, unsigned int clear)
+{
+       int control = 0;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (set & TIOCM_RTS) {
+               control |= CONTROL_RTS;
+               control |= CONTROL_WRITE_RTS;
+       }
+       if (set & TIOCM_DTR) {
+               control |= CONTROL_DTR;
+               control |= CONTROL_WRITE_DTR;
+       }
+       if (clear & TIOCM_RTS) {
+               control &= ~CONTROL_RTS;
+               control |= CONTROL_WRITE_RTS;
+       }
+       if (clear & TIOCM_DTR) {
+               control &= ~CONTROL_DTR;
+               control |= CONTROL_WRITE_DTR;
+       }
+
+       dbg("%s - control = 0x%.4x", __FUNCTION__, control);
+
+       return cp2101_set_config(port, CP2101_CONTROL, &control, 2);
+
+}
+
+static int cp2101_tiocmget (struct usb_serial_port *port, struct file *file)
+{
+       int control, result;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       cp2101_get_config(port, CP2101_CONTROL, &control, 1);
+
+       result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0)
+               |((control & CONTROL_RTS) ? TIOCM_RTS : 0)
+               |((control & CONTROL_CTS) ? TIOCM_CTS : 0)
+               |((control & CONTROL_DSR) ? TIOCM_DSR : 0)
+               |((control & CONTROL_RING)? TIOCM_RI  : 0)
+               |((control & CONTROL_DCD) ? TIOCM_CD  : 0);
+
+       dbg("%s - control = 0x%.2x", __FUNCTION__, control);
+
+       return result;
+}
+
+static void cp2101_break_ctl (struct usb_serial_port *port, int break_state)
+{
+       int state;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+       if (break_state == 0)
+               state = BREAK_OFF;
+       else
+               state = BREAK_ON;
+       dbg("%s - turning break %s", __FUNCTION__,
+                       state==BREAK_OFF ? "off" : "on");
+       cp2101_set_config(port, CP2101_BREAK, &state, 2);
+}
+
+static int cp2101_startup (struct usb_serial *serial)
+{
+       /* CP2101 buffers behave strangely unless device is reset */
+       usb_reset_device(serial->dev);
+       return 0;
+}
+
+static void cp2101_shutdown (struct usb_serial *serial)
+{
+       int i;
+
+       dbg("%s", __FUNCTION__);
+
+       /* Stop reads and writes on all ports */
+       for (i=0; i < serial->num_ports; ++i) {
+               cp2101_cleanup(serial->port[i]);
+       }
+}
+
+static int __init cp2101_init (void)
+{
+       int retval;
+
+       retval = usb_serial_register(&cp2101_device);
+       if (retval)
+               return retval; /* Failed to register */
+
+       retval = usb_register(&cp2101_driver);
+       if (retval) {
+               /* Failed to register */
+               usb_serial_deregister(&cp2101_device);
+               return retval;
+       }
+
+       /* Success */
+       info(DRIVER_DESC " " DRIVER_VERSION);
+       return 0;
+}
+
+static void __exit cp2101_exit (void)
+{
+       usb_deregister (&cp2101_driver);
+       usb_serial_deregister (&cp2101_device);
+}
+
+module_init(cp2101_init);
+module_exit(cp2101_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable verbose debugging messages");
diff --git a/drivers/usb/serial/hp4x.c b/drivers/usb/serial/hp4x.c
new file mode 100644 (file)
index 0000000..64d55fb
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * HP4x Calculators Serial USB driver
+ *
+ * Copyright (C) 2005 Arthur Huillet (ahuillet@users.sf.net)
+ * Copyright (C) 2001-2005 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.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include "usb-serial.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.00"
+#define DRIVER_DESC "HP4x (48/49) Generic Serial driver"
+
+#define HP_VENDOR_ID 0x03f0
+#define HP49GP_PRODUCT_ID 0x0121
+
+static struct usb_device_id id_table [] = {
+       { USB_DEVICE(HP_VENDOR_ID, HP49GP_PRODUCT_ID) },
+       { }                                     /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver hp49gp_driver = {
+       .owner =        THIS_MODULE,
+       .name =         "HP4X",
+       .probe =        usb_serial_probe,
+       .disconnect =   usb_serial_disconnect,
+       .id_table =     id_table,
+};
+
+static struct usb_serial_device_type hp49gp_device = {
+       .owner =                THIS_MODULE,
+       .name =                 "HP4X",
+       .id_table =             id_table,
+       .num_interrupt_in =     NUM_DONT_CARE,
+       .num_bulk_in =          NUM_DONT_CARE,
+       .num_bulk_out =         NUM_DONT_CARE,
+       .num_ports =            1,
+};
+
+static int __init hp49gp_init(void)
+{
+       int retval;
+       retval = usb_serial_register(&hp49gp_device);
+       if (retval)
+               goto failed_usb_serial_register;
+       retval = usb_register(&hp49gp_driver);
+       if (retval)
+               goto failed_usb_register;
+       info(DRIVER_DESC " " DRIVER_VERSION);
+       return 0;
+failed_usb_register:
+       usb_serial_deregister(&hp49gp_device);
+failed_usb_serial_register:
+       return retval;
+}
+
+static void __exit hp49gp_exit(void)
+{
+       usb_deregister(&hp49gp_driver);
+       usb_serial_deregister(&hp49gp_device);
+}
+
+module_init(hp49gp_init);
+module_exit(hp49gp_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
new file mode 100644 (file)
index 0000000..b722175
--- /dev/null
@@ -0,0 +1,729 @@
+/*
+  Option Card (PCMCIA to) USB to Serial Driver
+
+  Copyright (C) 2005  Matthias Urlichs <smurf@smurf.noris.de>
+
+  This driver 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.
+
+  Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org>
+
+  History:
+
+  2005-05-19  v0.1   Initial version, based on incomplete docs
+                     and analysis of misbehavior of the standard driver
+  2005-05-20  v0.2   Extended the input buffer to avoid losing
+                     random 64-byte chunks of data
+  2005-05-21  v0.3   implemented chars_in_buffer()
+                     turned on low_latency
+                     simplified the code somewhat
+*/
+#define DRIVER_VERSION "v0.3"
+#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
+#define DRIVER_DESC "Option Card (PC-Card to) USB to Serial Driver"
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/jiffies.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include "usb-serial.h"
+
+/* Function prototypes */
+static int  option_open (struct usb_serial_port *port, struct file *filp);
+static void option_close (struct usb_serial_port *port, struct file *filp);
+static int  option_startup (struct usb_serial *serial);
+static void option_shutdown (struct usb_serial *serial);
+static void option_rx_throttle (struct usb_serial_port *port);
+static void option_rx_unthrottle (struct usb_serial_port *port);
+static int  option_write_room (struct usb_serial_port *port);
+
+static void option_instat_callback(struct urb *urb, struct pt_regs *regs);
+
+
+static int  option_write (struct usb_serial_port *port,
+                          const unsigned char *buf, int count);
+
+static int  option_chars_in_buffer (struct usb_serial_port *port);
+static int  option_ioctl (struct usb_serial_port *port, struct file *file,
+                          unsigned int cmd, unsigned long arg);
+static void option_set_termios (struct usb_serial_port *port,
+                                struct termios *old);
+static void option_break_ctl (struct usb_serial_port *port, int break_state);
+static int  option_tiocmget (struct usb_serial_port *port, struct file *file);
+static int  option_tiocmset (struct usb_serial_port *port, struct file *file,
+                             unsigned int set, unsigned int clear);
+static int  option_send_setup (struct usb_serial_port *port);
+
+/* Vendor and product IDs */
+#define OPTION_VENDOR_ID               0x0AF0
+
+#define        OPTION_PRODUCT_OLD              0x5000
+#define        OPTION_PRODUCT_WLAN             0x6000
+
+static struct usb_device_id option_ids[] = {
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) },
+       { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_WLAN) },
+       { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, option_ids);
+
+static struct usb_driver option_driver = {
+       .owner      = THIS_MODULE,
+       .name       = "option",
+       .probe      = usb_serial_probe,
+       .disconnect = usb_serial_disconnect,
+       .id_table   = option_ids,
+};
+
+/* The card has three separate interfaces, wich the serial driver
+ * recognizes separately, thus num_port=1.
+ */
+static struct usb_serial_device_type option_3port_device = {
+       .owner                  = THIS_MODULE,
+       .name                   = "Option 3-port card",
+       .short_name             = "option",
+       .id_table               = option_ids,
+       .num_interrupt_in       = NUM_DONT_CARE,
+       .num_bulk_in            = NUM_DONT_CARE,
+       .num_bulk_out           = NUM_DONT_CARE,
+       .num_ports              = 1, /* 3 */
+       .open                   = option_open,
+       .close                  = option_close,
+       .write                  = option_write,
+       .write_room             = option_write_room,
+       .chars_in_buffer        = option_chars_in_buffer,
+       .throttle               = option_rx_throttle,
+       .unthrottle             = option_rx_unthrottle,
+       .ioctl                  = option_ioctl,
+       .set_termios            = option_set_termios,
+       .break_ctl              = option_break_ctl,
+       .tiocmget               = option_tiocmget,
+       .tiocmset               = option_tiocmset,
+       .attach                 = option_startup,
+       .shutdown               = option_shutdown,
+       .read_int_callback      = option_instat_callback,
+};
+
+static int debug;
+
+/* per port private data */
+
+#define N_IN_URB       4
+#define N_OUT_URB      1
+#define IN_BUFLEN      1024
+#define OUT_BUFLEN     1024
+
+struct option_port_private {
+       /* Input endpoints and buffer for this port */
+       struct urb      *in_urbs[N_IN_URB];
+       char            in_buffer[N_IN_URB][IN_BUFLEN];
+       /* Output endpoints and buffer for this port */
+       struct urb      *out_urbs[N_OUT_URB];
+       char            out_buffer[N_OUT_URB][OUT_BUFLEN];
+
+       /* Settings for the port */
+       int             rts_state;      /* Handshaking pins (outputs) */
+       int             dtr_state;
+       int             cts_state;      /* Handshaking pins (inputs) */
+       int             dsr_state;
+       int             dcd_state;
+       int             ri_state;
+       // int          break_on;
+
+       unsigned long   tx_start_time[N_OUT_URB];
+};
+
+
+/* Functions used by new usb-serial code. */
+static int __init
+option_init (void)
+{
+       int retval;
+       retval = usb_serial_register(&option_3port_device);
+       if (retval)
+               goto failed_3port_device_register;
+       retval = usb_register(&option_driver);
+       if (retval)
+               goto failed_driver_register;
+
+       info(DRIVER_DESC ": " DRIVER_VERSION);
+
+       return 0;
+
+failed_driver_register:
+       usb_serial_deregister (&option_3port_device);
+failed_3port_device_register:
+       return retval;
+}
+
+static void __exit
+option_exit (void)
+{
+       usb_deregister (&option_driver);
+       usb_serial_deregister (&option_3port_device);
+}
+
+module_init(option_init);
+module_exit(option_exit);
+
+static void
+option_rx_throttle (struct usb_serial_port *port)
+{
+       dbg("%s", __FUNCTION__);
+}
+
+
+static void
+option_rx_unthrottle (struct usb_serial_port *port)
+{
+       dbg("%s", __FUNCTION__);
+}
+
+
+static void
+option_break_ctl (struct usb_serial_port *port, int break_state)
+{
+       /* Unfortunately, I don't know how to send a break */
+       dbg("%s", __FUNCTION__);
+}
+
+
+static void
+option_set_termios (struct usb_serial_port *port,
+                                    struct termios *old_termios)
+{
+       dbg("%s", __FUNCTION__);
+
+       option_send_setup(port);
+}
+
+static int
+option_tiocmget(struct usb_serial_port *port, struct file *file)
+{
+       unsigned int                    value;
+       struct option_port_private      *portdata;
+
+       portdata = usb_get_serial_port_data(port);
+
+       value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
+               ((portdata->dtr_state) ? TIOCM_DTR : 0) |
+               ((portdata->cts_state) ? TIOCM_CTS : 0) |
+               ((portdata->dsr_state) ? TIOCM_DSR : 0) |
+               ((portdata->dcd_state) ? TIOCM_CAR : 0) |
+               ((portdata->ri_state) ? TIOCM_RNG : 0);
+
+       return value;
+}
+
+static int
+option_tiocmset (struct usb_serial_port *port, struct file *file,
+                 unsigned int set, unsigned int clear)
+{
+       struct option_port_private      *portdata;
+
+       portdata = usb_get_serial_port_data(port);
+
+       if (set & TIOCM_RTS)
+               portdata->rts_state = 1;
+       if (set & TIOCM_DTR)
+               portdata->dtr_state = 1;
+
+       if (clear & TIOCM_RTS)
+               portdata->rts_state = 0;
+       if (clear & TIOCM_DTR)
+               portdata->dtr_state = 0;
+       return option_send_setup(port);
+}
+
+static int
+option_ioctl (struct usb_serial_port *port, struct file *file,
+              unsigned int cmd, unsigned long arg)
+{
+       return -ENOIOCTLCMD;
+}
+
+/* Write */
+static int
+option_write(struct usb_serial_port *port,
+                        const unsigned char *buf, int count)
+{
+       struct option_port_private      *portdata;
+       int                             i;
+       int                             left, todo;
+       struct urb                      *this_urb = NULL; /* spurious */
+       int                             err;
+
+       portdata = usb_get_serial_port_data(port);
+
+       dbg("%s: write (%d chars)", __FUNCTION__, count);
+
+#if 0
+       spin_lock(&port->lock);
+       if (port->write_urb_busy) {
+               spin_unlock(&port->lock);
+               dbg("%s: already writing", __FUNCTION__);
+               return 0;
+       }
+       port->write_urb_busy = 1;
+       spin_unlock(&port->lock);
+#endif
+
+       i = 0;
+       left = count;
+       while (left>0) {
+               todo = left;
+               if (todo > OUT_BUFLEN)
+                       todo = OUT_BUFLEN;
+
+               for (;i < N_OUT_URB; i++) {
+                       /* Check we have a valid urb/endpoint before we use it... */
+                       this_urb = portdata->out_urbs[i];
+                       if (this_urb->status != -EINPROGRESS)
+                               break;
+                       if (this_urb->transfer_flags & URB_ASYNC_UNLINK)
+                               continue;
+                       if (time_before(jiffies, portdata->tx_start_time[i] + 10 * HZ))
+                               continue;
+                       this_urb->transfer_flags |= URB_ASYNC_UNLINK;
+                       usb_unlink_urb(this_urb);
+               }
+
+               if (i == N_OUT_URB) {
+                       /* no bulk out free! */
+                       dbg("%s: no output urb -- left %d", __FUNCTION__,count-left);
+#if 0
+                       port->write_urb_busy = 0;
+#endif
+                       return count-left;
+               }
+
+               dbg("%s: endpoint %d buf %d", __FUNCTION__, usb_pipeendpoint(this_urb->pipe), i);
+
+               memcpy (this_urb->transfer_buffer, buf, todo);
+
+               /* send the data out the bulk port */
+               this_urb->transfer_buffer_length = todo;
+
+               this_urb->transfer_flags &= ~URB_ASYNC_UNLINK;
+               this_urb->dev = port->serial->dev;
+               err = usb_submit_urb(this_urb, GFP_ATOMIC);
+               if (err) {
+                       dbg("usb_submit_urb %p (write bulk) failed (%d,, has %d)", this_urb, err, this_urb->status);
+                       continue;
+               }
+               portdata->tx_start_time[i] = jiffies;
+               buf += todo;
+               left -= todo;
+       }
+
+       count -= left;
+#if 0
+       port->write_urb_busy = 0;
+#endif
+       dbg("%s: wrote (did %d)", __FUNCTION__, count);
+       return count;
+}
+
+static void
+option_indat_callback (struct urb *urb, struct pt_regs *regs)
+{
+       int     i, err;
+       int endpoint;
+       struct usb_serial_port *port;
+       struct tty_struct *tty;
+       unsigned char *data = urb->transfer_buffer;
+
+       dbg("%s: %p", __FUNCTION__, urb);
+
+       endpoint = usb_pipeendpoint(urb->pipe);
+       port = (struct usb_serial_port *) urb->context;
+
+       if (urb->status) {
+               dbg("%s: nonzero status: %d on endpoint %02x.",
+                   __FUNCTION__, urb->status, endpoint);
+       } else {
+               tty = port->tty;
+               if (urb->actual_length) {
+                       for (i = 0; i < urb->actual_length ; ++i) {
+                               if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+                                       tty_flip_buffer_push(tty);
+                               tty_insert_flip_char(tty, data[i], 0);
+                       }
+                       tty_flip_buffer_push(tty);
+               } else {
+                       dbg("%s: empty read urb received", __FUNCTION__);
+               }
+
+               /* Resubmit urb so we continue receiving */
+               if (port->open_count && urb->status != -ESHUTDOWN) {
+                       err = usb_submit_urb(urb, GFP_ATOMIC);
+                       if (err)
+                               printk(KERN_ERR "%s: resubmit read urb failed. (%d)", __FUNCTION__, err);
+               }
+       }
+       return;
+}
+
+static void
+option_outdat_callback (struct urb *urb, struct pt_regs *regs)
+{
+       struct usb_serial_port *port;
+
+       dbg("%s", __FUNCTION__);
+
+       port = (struct usb_serial_port *) urb->context;
+
+       if (port->open_count)
+               schedule_work(&port->work);
+}
+
+static void
+option_instat_callback (struct urb *urb, struct pt_regs *regs)
+{
+       int err;
+       struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
+       struct option_port_private *portdata = usb_get_serial_port_data(port);
+       struct usb_serial *serial = port->serial;
+
+       dbg("%s", __FUNCTION__);
+       dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata);
+
+       if (urb->status == 0) {
+               struct usb_ctrlrequest *req_pkt =
+                               (struct usb_ctrlrequest *)urb->transfer_buffer;
+
+               if (!req_pkt) {
+                       dbg("%s: NULL req_pkt\n", __FUNCTION__);
+                       return;
+               }
+               if ((req_pkt->bRequestType == 0xA1) && (req_pkt->bRequest == 0x20)) {
+                       int old_dcd_state;
+                       unsigned char signals = *((unsigned char *)
+                                       urb->transfer_buffer + sizeof(struct usb_ctrlrequest));
+
+                       dbg("%s: signal x%x", __FUNCTION__, signals);
+
+                       old_dcd_state = portdata->dcd_state;
+                       portdata->cts_state = 1;
+                       portdata->dcd_state = ((signals & 0x01) ? 1 : 0);
+                       portdata->dsr_state = ((signals & 0x02) ? 1 : 0);
+                       portdata->ri_state = ((signals & 0x08) ? 1 : 0);
+
+                       if (port->tty && !C_CLOCAL(port->tty)
+                                       && old_dcd_state && !portdata->dcd_state) {
+                               tty_hangup(port->tty);
+                       }
+               } else
+                       dbg("%s: type %x req %x", __FUNCTION__, req_pkt->bRequestType,req_pkt->bRequest);
+       } else
+               dbg("%s: error %d", __FUNCTION__, urb->status);
+
+       /* Resubmit urb so we continue receiving IRQ data */
+       if (urb->status != -ESHUTDOWN) {
+               urb->dev = serial->dev;
+               err = usb_submit_urb(urb, GFP_ATOMIC);
+               if (err)
+                       dbg("%s: resubmit intr urb failed. (%d)", __FUNCTION__, err);
+       }
+}
+
+
+static int
+option_write_room (struct usb_serial_port *port)
+{
+       struct option_port_private *portdata;
+       int i;
+       int data_len = 0;
+       struct urb *this_urb;
+
+       portdata = usb_get_serial_port_data(port);
+
+       for (i=0; i < N_OUT_URB; i++)
+               this_urb = portdata->out_urbs[i];
+               if (this_urb && this_urb->status != -EINPROGRESS)
+                       data_len += OUT_BUFLEN;
+
+       dbg("%s: %d", __FUNCTION__, data_len);
+       return data_len;
+}
+
+
+static int
+option_chars_in_buffer (struct usb_serial_port *port)
+{
+       struct option_port_private *portdata;
+       int i;
+       int data_len = 0;
+       struct urb *this_urb;
+
+       portdata = usb_get_serial_port_data(port);
+
+       for (i=0; i < N_OUT_URB; i++)
+               this_urb = portdata->out_urbs[i];
+               if (this_urb && this_urb->status == -EINPROGRESS)
+                       data_len += this_urb->transfer_buffer_length;
+
+       dbg("%s: %d", __FUNCTION__, data_len);
+       return data_len;
+}
+
+
+static int
+option_open (struct usb_serial_port *port, struct file *filp)
+{
+       struct option_port_private      *portdata;
+       struct usb_serial               *serial = port->serial;
+       int                             i, err;
+       struct urb                      *urb;
+
+       portdata = usb_get_serial_port_data(port);
+
+       dbg("%s", __FUNCTION__);
+
+       /* Set some sane defaults */
+       portdata->rts_state = 1;
+       portdata->dtr_state = 1;
+
+       /* Reset low level data toggle and start reading from endpoints */
+       for (i = 0; i < N_IN_URB; i++) {
+               urb = portdata->in_urbs[i];
+               if (! urb)
+                       continue;
+               if (urb->dev != serial->dev) {
+                       dbg("%s: dev %p != %p", __FUNCTION__, urb->dev, serial->dev);
+                       continue;
+               }
+
+               /* make sure endpoint data toggle is synchronized with the device */
+
+               usb_clear_halt(urb->dev, urb->pipe);
+
+               err = usb_submit_urb(urb, GFP_KERNEL);
+               if (err) {
+                       dbg("%s: submit urb %d failed (%d) %d", __FUNCTION__, i, err,
+                               urb->transfer_buffer_length);
+               }
+       }
+
+       /* Reset low level data toggle on out endpoints */
+       for (i = 0; i < N_OUT_URB; i++) {
+               urb = portdata->out_urbs[i];
+               if (! urb)
+                       continue;
+               urb->dev = serial->dev;
+               /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 0); */
+       }
+
+       port->tty->low_latency = 1;
+
+       option_send_setup(port);
+
+       return (0);
+}
+
+static inline void
+stop_urb(struct urb *urb)
+{
+       if (urb && urb->status == -EINPROGRESS) {
+               urb->transfer_flags &= ~URB_ASYNC_UNLINK;
+               usb_kill_urb(urb);
+       }
+}
+
+static void
+option_close(struct usb_serial_port *port, struct file *filp)
+{
+       int                     i;
+       struct usb_serial       *serial = port->serial;
+       struct option_port_private      *portdata;
+
+       dbg("%s", __FUNCTION__);
+       portdata = usb_get_serial_port_data(port);
+
+       portdata->rts_state = 0;
+       portdata->dtr_state = 0;
+
+       if (serial->dev) {
+               option_send_setup(port);
+
+               /* Stop reading/writing urbs */
+               for (i = 0; i < N_IN_URB; i++)
+                       stop_urb(portdata->in_urbs[i]);
+               for (i = 0; i < N_OUT_URB; i++)
+                       stop_urb(portdata->out_urbs[i]);
+       }
+       port->tty = NULL;
+}
+
+
+/* Helper functions used by option_setup_urbs */
+static struct urb *
+option_setup_urb (struct usb_serial *serial, int endpoint,
+                  int dir, void *ctx, char *buf, int len,
+                  void (*callback)(struct urb *, struct pt_regs *regs))
+{
+       struct urb *urb;
+
+       if (endpoint == -1)
+               return NULL;            /* endpoint not needed */
+
+       urb = usb_alloc_urb(0, GFP_KERNEL);             /* No ISO */
+       if (urb == NULL) {
+               dbg("%s: alloc for endpoint %d failed.", __FUNCTION__, endpoint);
+               return NULL;
+       }
+
+               /* Fill URB using supplied data. */
+       usb_fill_bulk_urb(urb, serial->dev,
+                     usb_sndbulkpipe(serial->dev, endpoint) | dir,
+                     buf, len, callback, ctx);
+
+       return urb;
+}
+
+/* Setup urbs */
+static void
+option_setup_urbs(struct usb_serial *serial)
+{
+       int                             j;
+       struct usb_serial_port          *port;
+       struct option_port_private      *portdata;
+
+       dbg("%s", __FUNCTION__);
+
+       port = serial->port[0];
+       portdata = usb_get_serial_port_data(port);
+
+       /* Do indat endpoints first */
+       for (j = 0; j <= N_IN_URB; ++j) {
+               portdata->in_urbs[j] = option_setup_urb (serial,
+                  port->bulk_in_endpointAddress, USB_DIR_IN, port,
+                  portdata->in_buffer[j], IN_BUFLEN, option_indat_callback);
+       }
+
+       /* outdat endpoints */
+       for (j = 0; j <= N_OUT_URB; ++j) {
+               portdata->out_urbs[j] = option_setup_urb (serial,
+                  port->bulk_out_endpointAddress, USB_DIR_OUT, port,
+                  portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback);
+       }
+}
+
+
+static int
+option_send_setup(struct usb_serial_port *port)
+{
+       struct usb_serial *serial = port->serial;
+       struct option_port_private *portdata;
+
+       dbg("%s", __FUNCTION__);
+
+       portdata = usb_get_serial_port_data(port);
+
+       if (port->tty) {
+               int val = 0;
+               if (portdata->dtr_state)
+                       val |= 0x01;
+               if (portdata->rts_state)
+                       val |= 0x02;
+
+               return usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+                                       0x22,0x21,val,0,NULL,0,USB_CTRL_SET_TIMEOUT);
+       }
+
+       return 0;
+}
+
+
+static int
+option_startup (struct usb_serial *serial)
+{
+       int                             i, err;
+       struct usb_serial_port          *port;
+       struct option_port_private      *portdata;
+
+       dbg("%s", __FUNCTION__);
+
+       /* Now setup per port private data */
+       for (i = 0; i < serial->num_ports; i++) {
+               port = serial->port[i];
+               portdata = kmalloc(sizeof(struct option_port_private), GFP_KERNEL);
+               if (!portdata) {
+                       dbg("%s: kmalloc for option_port_private (%d) failed!.", __FUNCTION__, i);
+                       return (1);
+               }
+               memset(portdata, 0, sizeof(struct option_port_private));
+
+               usb_set_serial_port_data(port, portdata);
+
+               if (! port->interrupt_in_urb)
+                       continue;
+               err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+               if (err)
+                       dbg("%s: submit irq_in urb failed %d", __FUNCTION__, err);
+       }
+
+       option_setup_urbs(serial);
+
+       return (0);
+}
+
+static void
+option_shutdown (struct usb_serial *serial)
+{
+       int                             i, j;
+       struct usb_serial_port          *port;
+       struct option_port_private      *portdata;
+
+       dbg("%s", __FUNCTION__);
+
+       /* Stop reading/writing urbs */
+       for (i = 0; i < serial->num_ports; ++i) {
+               port = serial->port[i];
+               portdata = usb_get_serial_port_data(port);
+               for (j = 0; j < N_IN_URB; j++)
+                       stop_urb(portdata->in_urbs[j]);
+               for (j = 0; j < N_OUT_URB; j++)
+                       stop_urb(portdata->out_urbs[j]);
+       }
+
+       /* Now free them */
+       for (i = 0; i < serial->num_ports; ++i) {
+               port = serial->port[i];
+               portdata = usb_get_serial_port_data(port);
+
+               for (j = 0; j < N_IN_URB; j++) {
+                       if (portdata->in_urbs[j]) {
+                               usb_free_urb(portdata->in_urbs[j]);
+                               portdata->in_urbs[j] = NULL;
+                       }
+               }
+               for (j = 0; j < N_OUT_URB; j++) {
+                       if (portdata->out_urbs[j]) {
+                               usb_free_urb(portdata->out_urbs[j]);
+                               portdata->out_urbs[j] = NULL;
+                       }
+               }
+       }
+
+       /* Now free per port private data */
+       for (i = 0; i < serial->num_ports; i++) {
+               port = serial->port[i];
+               kfree(usb_get_serial_port_data(port));
+       }
+}
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug messages");
+
diff --git a/drivers/video/geode/Kconfig b/drivers/video/geode/Kconfig
new file mode 100644 (file)
index 0000000..b075fd0
--- /dev/null
@@ -0,0 +1,29 @@
+#
+# Geode family framebuffer configuration
+#
+config FB_GEODE
+       bool "AMD Geode family framebuffer support (EXPERIMENTAL)"
+       default n
+       depends on FB && EXPERIMENTAL && X86
+       ---help---
+         Say 'Y' here to allow you to select framebuffer drivers for
+         the AMD Geode family of processors.
+
+config FB_GEODE_GX1
+       tristate "AMD Geode GX1 framebuffer support (EXPERIMENTAL)"
+       default n
+       depends on FB_GEODE && EXPERIMENTAL
+       select FB_CFB_FILLRECT
+       select FB_CFB_COPYAREA
+       select FB_CFB_IMAGEBLIT
+       select FB_SOFT_CURSOR
+       ---help---
+         Framebuffer driver for the display controller integrated into the
+         AMD Geode GX1 processor.
+
+         This driver is also available as a module ( = code which can be
+         inserted and removed from the running kernel whenever you want). The
+         module will be called gx1fb. If you want to compile it as a module,
+         say M here and read <file:Documentation/modules.txt>.
+
+         If unsure, say N.
diff --git a/drivers/video/geode/Makefile b/drivers/video/geode/Makefile
new file mode 100644 (file)
index 0000000..13ad501
--- /dev/null
@@ -0,0 +1,5 @@
+# Makefile for the Geode family framebuffer drivers
+
+obj-$(CONFIG_FB_GEODE_GX1) += gx1fb.o
+
+gx1fb-objs             := gx1fb_core.o display_gx1.o video_cs5530.o
diff --git a/drivers/video/geode/gx1fb_core.c b/drivers/video/geode/gx1fb_core.c
new file mode 100644 (file)
index 0000000..83830d2
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * drivers/video/geode/gx1fb_core.c
+ *   -- Geode GX1 framebuffer driver
+ *
+ * Copyright (C) 2005 Arcom Control Systems 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.
+ */
+
+#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 "geodefb.h"
+#include "display_gx1.h"
+#include "video_cs5530.h"
+
+static char mode_option[32] = "640x480-16@60";
+static int  crt_option = 1;
+static char panel_option[32] = "";
+
+static int gx1_line_delta(int xres, int bpp)
+{
+       int line_delta = xres * (bpp >> 3);
+
+       if (line_delta > 2048)
+               line_delta = 4096;
+       else if (line_delta > 1024)
+               line_delta = 2048;
+       else
+               line_delta = 1024;
+       return line_delta;
+}
+
+static int gx1fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       struct geodefb_par *par = info->par;
+
+       printk(KERN_DEBUG "%s()\n", __FUNCTION__);
+
+       /* Maximum resolution is 1280x1024. */
+       if (var->xres > 1280 || var->yres > 1024)
+               return -EINVAL;
+
+       if (par->panel_x && (var->xres > par->panel_x || var->yres > par->panel_y))
+               return -EINVAL;
+
+       /* Only 16 bpp and 8 bpp is supported by the hardware. */
+       if (var->bits_per_pixel == 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;
+               var->transp.offset = 0; var->transp.length = 0;
+       } else if (var->bits_per_pixel == 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;
+       } else
+               return -EINVAL;
+
+       /* Enough video memory? */
+       if (gx1_line_delta(var->xres, var->bits_per_pixel) * var->yres > info->fix.smem_len)
+               return -EINVAL;
+
+       /* FIXME: Check timing parameters here? */
+
+       return 0;
+}
+
+static int gx1fb_set_par(struct fb_info *info)
+{
+       struct geodefb_par *par = info->par;
+
+       if (info->var.bits_per_pixel == 16) {
+               info->fix.visual = FB_VISUAL_TRUECOLOR;
+               fb_dealloc_cmap(&info->cmap);
+       } else {
+               info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+               fb_alloc_cmap(&info->cmap, 1<<info->var.bits_per_pixel, 0);
+       }
+
+       info->fix.line_length = gx1_line_delta(info->var.xres, info->var.bits_per_pixel);
+
+       par->dc_ops->set_mode(info);
+
+       return 0;
+}
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+       chan &= 0xffff;
+       chan >>= 16 - bf->length;
+       return chan << bf->offset;
+}
+
+static int gx1fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+                          unsigned blue, unsigned transp,
+                          struct fb_info *info)
+{
+       struct geodefb_par *par = info->par;
+
+       if (info->var.grayscale) {
+               /* grayscale = 0.30*R + 0.59*G + 0.11*B */
+               red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
+       }
+
+       /* Truecolor has hardware independent palette */
+       if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
+               u32 *pal = info->pseudo_palette;
+               u32 v;
+
+               if (regno >= 16)
+                       return -EINVAL;
+
+               v  = chan_to_field(red, &info->var.red);
+               v |= chan_to_field(green, &info->var.green);
+               v |= chan_to_field(blue, &info->var.blue);
+
+               pal[regno] = v;
+       } else {
+               if (regno >= 256)
+                       return -EINVAL;
+
+               par->dc_ops->set_palette_reg(info, regno, red, green, blue);
+       }
+
+       return 0;
+}
+
+static int gx1fb_blank(int blank_mode, struct fb_info *info)
+{
+       struct geodefb_par *par = info->par;
+
+       return par->vid_ops->blank_display(info, blank_mode);
+}
+
+static int __init gx1fb_map_video_memory(struct fb_info *info)
+{
+       struct geodefb_par *par = info->par;
+       unsigned gx_base;
+       int fb_len;
+
+       gx_base = gx1_gx_base();
+       if (!gx_base)
+               return -ENODEV;
+
+       par->vid_dev = pci_get_device(PCI_VENDOR_ID_CYRIX,
+                                     PCI_DEVICE_ID_CYRIX_5530_VIDEO, NULL);
+       if (!par->vid_dev)
+               return -ENODEV;
+
+       par->vid_regs = ioremap(pci_resource_start(par->vid_dev, 1),
+                               pci_resource_len(par->vid_dev, 1));
+       if (!par->vid_regs)
+               return -ENOMEM;
+
+       par->dc_regs = ioremap(gx_base + 0x8300, 0x100);
+       if (!par->dc_regs)
+               return -ENOMEM;
+
+       info->fix.smem_start = gx_base + 0x800000;
+       if ((fb_len = gx1_frame_buffer_size()) < 0)
+               return -ENOMEM;
+       info->fix.smem_len = fb_len;
+       info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
+       if (!info->screen_base)
+               return -ENOMEM;
+
+       printk(KERN_INFO "%s: %d Kibyte of video memory at 0x%lx\n",
+              info->fix.id, info->fix.smem_len / 1024, info->fix.smem_start);
+
+       return 0;
+}
+
+static int parse_panel_option(struct fb_info *info)
+{
+       struct geodefb_par *par = info->par;
+
+       if (strcmp(panel_option, "") != 0) {
+               int x, y;
+               char *s;
+               x = simple_strtol(panel_option, &s, 10);
+               if (!x)
+                       return -EINVAL;
+               y = simple_strtol(s + 1, NULL, 10);
+               if (!y)
+                       return -EINVAL;
+               par->panel_x = x;
+               par->panel_y = y;
+       }
+       return 0;
+}
+
+static struct fb_ops gx1fb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_check_var   = gx1fb_check_var,
+       .fb_set_par     = gx1fb_set_par,
+       .fb_setcolreg   = gx1fb_setcolreg,
+       .fb_blank       = gx1fb_blank,
+       /* No HW acceleration for now. */
+       .fb_fillrect    = cfb_fillrect,
+       .fb_copyarea    = cfb_copyarea,
+       .fb_imageblit   = cfb_imageblit,
+       .fb_cursor      = soft_cursor,
+};
+
+static struct fb_info * __init gx1fb_init_fbinfo(void)
+{
+       struct fb_info *info;
+       struct geodefb_par *par;
+
+       /* Alloc enough space for the pseudo palette. */
+       info = framebuffer_alloc(sizeof(struct geodefb_par) + sizeof(u32) * 16, NULL);
+       if (!info)
+               return NULL;
+
+       par = info->par;
+
+       strcpy(info->fix.id, "GX1");
+
+       info->fix.type          = FB_TYPE_PACKED_PIXELS;
+       info->fix.type_aux      = 0;
+       info->fix.xpanstep      = 0;
+       info->fix.ypanstep      = 0;
+       info->fix.ywrapstep     = 0;
+       info->fix.accel         = FB_ACCEL_NONE;
+
+       info->var.nonstd        = 0;
+       info->var.activate      = FB_ACTIVATE_NOW;
+       info->var.height        = -1;
+       info->var.width = -1;
+       info->var.accel_flags = 0;
+       info->var.vmode = FB_VMODE_NONINTERLACED;
+
+       info->fbops             = &gx1fb_ops;
+       info->flags             = FBINFO_DEFAULT;
+       info->node              = -1;
+
+       info->pseudo_palette    = (void *)par + sizeof(struct geodefb_par);
+
+       info->var.grayscale     = 0;
+
+       /* CRT and panel options */
+       par->enable_crt = crt_option;
+       if (parse_panel_option(info) < 0)
+               printk(KERN_WARNING "%s: invalid 'panel' option -- disabling flat panel\n",
+                      info->fix.id);
+       if (!par->panel_x)
+               par->enable_crt = 1; /* fall back to CRT if no panel is specified */
+
+       return info;
+}
+
+
+static struct fb_info *gx1fb_info;
+
+static int __init gx1fb_init(void)
+{
+       struct fb_info *info;
+        struct geodefb_par *par;
+       int ret;
+
+#ifndef MODULE
+       if (fb_get_options("gx1fb", NULL))
+               return -ENODEV;
+#endif
+
+       info = gx1fb_init_fbinfo();
+       if (!info)
+               return -ENOMEM;
+       gx1fb_info = info;
+
+       par = info->par;
+
+       /* GX1 display controller and CS5530 video device */
+       par->dc_ops  = &gx1_dc_ops;
+       par->vid_ops = &cs5530_vid_ops;
+
+       if ((ret = gx1fb_map_video_memory(info)) < 0) {
+               printk(KERN_ERR "%s: gx1fb_map_video_memory() failed\n", info->fix.id);
+               goto err;
+       }
+
+       ret = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 16);
+       if (ret == 0 || ret == 4) {
+               printk(KERN_ERR "%s: could not find valid video mode\n", info->fix.id);
+               ret = -EINVAL;
+               goto err;
+       }
+
+        /* Clear the frame buffer of garbage. */
+        memset_io(info->screen_base, 0, info->fix.smem_len);
+
+       gx1fb_check_var(&info->var, info);
+       gx1fb_set_par(info);
+
+       if (register_framebuffer(info) < 0) {
+               ret = -EINVAL;
+               goto err;
+       }
+       printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id);
+       return 0;
+
+  err:
+       if (info->screen_base)
+               iounmap(info->screen_base);
+       if (par->vid_regs)
+               iounmap(par->vid_regs);
+       if (par->dc_regs)
+               iounmap(par->dc_regs);
+       if (par->vid_dev)
+               pci_dev_put(par->vid_dev);
+       if (info)
+               framebuffer_release(info);
+       return ret;
+}
+
+static void __exit gx1fb_cleanup(void)
+{
+       struct fb_info *info = gx1fb_info;
+       struct geodefb_par *par = gx1fb_info->par;
+
+       unregister_framebuffer(info);
+
+       iounmap((void __iomem *)info->screen_base);
+       iounmap(par->vid_regs);
+       iounmap(par->dc_regs);
+
+       pci_dev_put(par->vid_dev);
+
+       framebuffer_release(info);
+}
+
+module_init(gx1fb_init);
+module_exit(gx1fb_cleanup);
+
+module_param_string(mode, mode_option, sizeof(mode_option), 0444);
+MODULE_PARM_DESC(mode, "video mode (<x>x<y>[-<bpp>][@<refr>])");
+
+module_param_named(crt, crt_option, int, 0444);
+MODULE_PARM_DESC(crt, "enable CRT output. 0 = off, 1 = on (default)");
+
+module_param_string(panel, panel_option, sizeof(panel_option), 0444);
+MODULE_PARM_DESC(panel, "size of attached flat panel (<x>x<y>)");
+
+MODULE_DESCRIPTION("framebuffer driver for the AMD Geode GX1");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
new file mode 100644 (file)
index 0000000..8fe1c12
--- /dev/null
@@ -0,0 +1,695 @@
+/*
+ *  linux/drivers/video/imxfb.c
+ *
+ *  Freescale i.MX Frame Buffer device driver
+ *
+ *  Copyright (C) 2004 Sascha Hauer, Pengutronix
+ *   Based on acornfb.c Copyright (C) Russell King.
+ *
+ * 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.
+ *
+ * Please direct your questions and comments on this driver to the following
+ * email address:
+ *
+ *     linux-arm-kernel@lists.arm.linux.org.uk
+ */
+
+//#define DEBUG 1
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/cpufreq.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/uaccess.h>
+#include <asm/arch/imxfb.h>
+
+/*
+ * Complain if VAR is out of range.
+ */
+#define DEBUG_VAR 1
+
+#include "imxfb.h"
+
+static struct imxfb_rgb def_rgb_16 = {
+       .red    = { .offset = 8,  .length = 4, },
+       .green  = { .offset = 4,  .length = 4, },
+       .blue   = { .offset = 0,  .length = 4, },
+       .transp = { .offset = 0,  .length = 0, },
+};
+
+static struct imxfb_rgb def_rgb_8 = {
+       .red    = { .offset = 0,  .length = 8, },
+       .green  = { .offset = 0,  .length = 8, },
+       .blue   = { .offset = 0,  .length = 8, },
+       .transp = { .offset = 0,  .length = 0, },
+};
+
+static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info);
+
+static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
+{
+       chan &= 0xffff;
+       chan >>= 16 - bf->length;
+       return chan << bf->offset;
+}
+
+#define LCDC_PALETTE(x) __REG2(IMX_LCDC_BASE+0x800, (x)<<2)
+static int
+imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
+                      u_int trans, struct fb_info *info)
+{
+       struct imxfb_info *fbi = info->par;
+       u_int val, ret = 1;
+
+#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
+       if (regno < fbi->palette_size) {
+               val = (CNVT_TOHW(red, 4) << 8) |
+                     (CNVT_TOHW(green,4) << 4) |
+                     CNVT_TOHW(blue,  4);
+
+               LCDC_PALETTE(regno) = val;
+               ret = 0;
+       }
+       return ret;
+}
+
+static int
+imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                  u_int trans, struct fb_info *info)
+{
+       struct imxfb_info *fbi = info->par;
+       unsigned int val;
+       int ret = 1;
+
+       /*
+        * If inverse mode was selected, invert all the colours
+        * rather than the register number.  The register number
+        * is what you poke into the framebuffer to produce the
+        * colour you requested.
+        */
+       if (fbi->cmap_inverse) {
+               red   = 0xffff - red;
+               green = 0xffff - green;
+               blue  = 0xffff - blue;
+       }
+
+       /*
+        * If greyscale is true, then we convert the RGB value
+        * to greyscale no mater what visual we are using.
+        */
+       if (info->var.grayscale)
+               red = green = blue = (19595 * red + 38470 * green +
+                                       7471 * blue) >> 16;
+
+       switch (info->fix.visual) {
+       case FB_VISUAL_TRUECOLOR:
+               /*
+                * 12 or 16-bit True Colour.  We encode the RGB value
+                * according to the RGB bitfield information.
+                */
+               if (regno < 16) {
+                       u32 *pal = info->pseudo_palette;
+
+                       val  = chan_to_field(red, &info->var.red);
+                       val |= chan_to_field(green, &info->var.green);
+                       val |= chan_to_field(blue, &info->var.blue);
+
+                       pal[regno] = val;
+                       ret = 0;
+               }
+               break;
+
+       case FB_VISUAL_STATIC_PSEUDOCOLOR:
+       case FB_VISUAL_PSEUDOCOLOR:
+               ret = imxfb_setpalettereg(regno, red, green, blue, trans, info);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ *  imxfb_check_var():
+ *    Round up in the following order: bits_per_pixel, xres,
+ *    yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
+ *    bitfields, horizontal timing, vertical timing.
+ */
+static int
+imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       struct imxfb_info *fbi = info->par;
+       int rgbidx;
+
+       if (var->xres < MIN_XRES)
+               var->xres = MIN_XRES;
+       if (var->yres < MIN_YRES)
+               var->yres = MIN_YRES;
+       if (var->xres > fbi->max_xres)
+               var->xres = fbi->max_xres;
+       if (var->yres > fbi->max_yres)
+               var->yres = fbi->max_yres;
+       var->xres_virtual = max(var->xres_virtual, var->xres);
+       var->yres_virtual = max(var->yres_virtual, var->yres);
+
+       pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel);
+       switch (var->bits_per_pixel) {
+       case 16:
+               rgbidx = RGB_16;
+               break;
+       case 8:
+               rgbidx = RGB_8;
+               break;
+       default:
+               rgbidx = RGB_16;
+       }
+
+       /*
+        * Copy the RGB parameters for this display
+        * from the machine specific parameters.
+        */
+       var->red    = fbi->rgb[rgbidx]->red;
+       var->green  = fbi->rgb[rgbidx]->green;
+       var->blue   = fbi->rgb[rgbidx]->blue;
+       var->transp = fbi->rgb[rgbidx]->transp;
+
+       pr_debug("RGBT length = %d:%d:%d:%d\n",
+               var->red.length, var->green.length, var->blue.length,
+               var->transp.length);
+
+       pr_debug("RGBT offset = %d:%d:%d:%d\n",
+               var->red.offset, var->green.offset, var->blue.offset,
+               var->transp.offset);
+
+       return 0;
+}
+
+/*
+ * imxfb_set_par():
+ *     Set the user defined part of the display for the specified console
+ */
+static int imxfb_set_par(struct fb_info *info)
+{
+       struct imxfb_info *fbi = info->par;
+       struct fb_var_screeninfo *var = &info->var;
+
+       pr_debug("set_par\n");
+
+       if (var->bits_per_pixel == 16)
+               info->fix.visual = FB_VISUAL_TRUECOLOR;
+       else if (!fbi->cmap_static)
+               info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+       else {
+               /*
+                * Some people have weird ideas about wanting static
+                * pseudocolor maps.  I suspect their user space
+                * applications are broken.
+                */
+               info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
+       }
+
+       info->fix.line_length = var->xres_virtual *
+                                 var->bits_per_pixel / 8;
+       fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16;
+
+       imxfb_activate_var(var, info);
+
+       return 0;
+}
+
+static void imxfb_enable_controller(struct imxfb_info *fbi)
+{
+       pr_debug("Enabling LCD controller\n");
+
+       /* initialize LCDC */
+       LCDC_RMCR &= ~RMCR_LCDC_EN;             /* just to be safe... */
+
+       LCDC_SSA        = fbi->screen_dma;
+       /* physical screen start address            */
+       LCDC_VPW        = VPW_VPW(fbi->max_xres * fbi->max_bpp / 8 / 4);
+
+       LCDC_POS        = 0x00000000;   /* panning offset 0 (0 pixel offset)        */
+
+       /* disable hardware cursor */
+       LCDC_CPOS       &= ~(CPOS_CC0 | CPOS_CC1);
+
+       /* fixed burst length (see erratum 11) */
+       LCDC_DMACR = DMACR_BURST | DMACR_HM(8) | DMACR_TM(2);
+
+       LCDC_RMCR = RMCR_LCDC_EN;
+
+       if(fbi->backlight_power)
+               fbi->backlight_power(1);
+       if(fbi->lcd_power)
+               fbi->lcd_power(1);
+}
+
+static void imxfb_disable_controller(struct imxfb_info *fbi)
+{
+       pr_debug("Disabling LCD controller\n");
+
+       if(fbi->backlight_power)
+               fbi->backlight_power(0);
+       if(fbi->lcd_power)
+               fbi->lcd_power(0);
+
+       LCDC_RMCR = 0;
+}
+
+static int imxfb_blank(int blank, struct fb_info *info)
+{
+       struct imxfb_info *fbi = info->par;
+
+       pr_debug("imxfb_blank: blank=%d\n", blank);
+
+       switch (blank) {
+       case FB_BLANK_POWERDOWN:
+       case FB_BLANK_VSYNC_SUSPEND:
+       case FB_BLANK_HSYNC_SUSPEND:
+       case FB_BLANK_NORMAL:
+               imxfb_disable_controller(fbi);
+               break;
+
+       case FB_BLANK_UNBLANK:
+               imxfb_enable_controller(fbi);
+               break;
+       }
+       return 0;
+}
+
+static struct fb_ops imxfb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_check_var   = imxfb_check_var,
+       .fb_set_par     = imxfb_set_par,
+       .fb_setcolreg   = imxfb_setcolreg,
+       .fb_fillrect    = cfb_fillrect,
+       .fb_copyarea    = cfb_copyarea,
+       .fb_imageblit   = cfb_imageblit,
+       .fb_blank       = imxfb_blank,
+       .fb_cursor      = soft_cursor, /* FIXME: i.MX can do hardware cursor */
+};
+
+/*
+ * imxfb_activate_var():
+ *     Configures LCD Controller based on entries in var parameter.  Settings are
+ *     only written to the controller if changes were made.
+ */
+static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       struct imxfb_info *fbi = info->par;
+       pr_debug("var: xres=%d hslen=%d lm=%d rm=%d\n",
+               var->xres, var->hsync_len,
+               var->left_margin, var->right_margin);
+       pr_debug("var: yres=%d vslen=%d um=%d bm=%d\n",
+               var->yres, var->vsync_len,
+               var->upper_margin, var->lower_margin);
+
+#if DEBUG_VAR
+       if (var->xres < 16        || var->xres > 1024)
+               printk(KERN_ERR "%s: invalid xres %d\n",
+                       info->fix.id, var->xres);
+       if (var->hsync_len < 1    || var->hsync_len > 64)
+               printk(KERN_ERR "%s: invalid hsync_len %d\n",
+                       info->fix.id, var->hsync_len);
+       if (var->left_margin > 255)
+               printk(KERN_ERR "%s: invalid left_margin %d\n",
+                       info->fix.id, var->left_margin);
+       if (var->right_margin > 255)
+               printk(KERN_ERR "%s: invalid right_margin %d\n",
+                       info->fix.id, var->right_margin);
+       if (var->yres < 1 || var->yres > 511)
+               printk(KERN_ERR "%s: invalid yres %d\n",
+                       info->fix.id, var->yres);
+       if (var->vsync_len > 100)
+               printk(KERN_ERR "%s: invalid vsync_len %d\n",
+                       info->fix.id, var->vsync_len);
+       if (var->upper_margin > 63)
+               printk(KERN_ERR "%s: invalid upper_margin %d\n",
+                       info->fix.id, var->upper_margin);
+       if (var->lower_margin > 255)
+               printk(KERN_ERR "%s: invalid lower_margin %d\n",
+                       info->fix.id, var->lower_margin);
+#endif
+
+       LCDC_HCR        = HCR_H_WIDTH(var->hsync_len) |
+                         HCR_H_WAIT_1(var->left_margin) |
+                         HCR_H_WAIT_2(var->right_margin);
+
+       LCDC_VCR        = VCR_V_WIDTH(var->vsync_len) |
+                         VCR_V_WAIT_1(var->upper_margin) |
+                         VCR_V_WAIT_2(var->lower_margin);
+
+       LCDC_SIZE       = SIZE_XMAX(var->xres) | SIZE_YMAX(var->yres);
+       LCDC_PCR        = fbi->pcr;
+       LCDC_PWMR       = fbi->pwmr;
+       LCDC_LSCR1      = fbi->lscr1;
+
+       return 0;
+}
+
+static void imxfb_setup_gpio(struct imxfb_info *fbi)
+{
+       int width;
+
+       LCDC_RMCR       &= ~(RMCR_LCDC_EN | RMCR_SELF_REF);
+
+       if( fbi->pcr & PCR_TFT )
+               width = 16;
+       else
+               width = 1 << ((fbi->pcr >> 28) & 0x3);
+
+       switch(width) {
+       case 16:
+               imx_gpio_mode(PD30_PF_LD15);
+               imx_gpio_mode(PD29_PF_LD14);
+               imx_gpio_mode(PD28_PF_LD13);
+               imx_gpio_mode(PD27_PF_LD12);
+               imx_gpio_mode(PD26_PF_LD11);
+               imx_gpio_mode(PD25_PF_LD10);
+               imx_gpio_mode(PD24_PF_LD9);
+               imx_gpio_mode(PD23_PF_LD8);
+       case 8:
+               imx_gpio_mode(PD22_PF_LD7);
+               imx_gpio_mode(PD21_PF_LD6);
+               imx_gpio_mode(PD20_PF_LD5);
+               imx_gpio_mode(PD19_PF_LD4);
+       case 4:
+               imx_gpio_mode(PD18_PF_LD3);
+               imx_gpio_mode(PD17_PF_LD2);
+       case 2:
+               imx_gpio_mode(PD16_PF_LD1);
+       case 1:
+               imx_gpio_mode(PD15_PF_LD0);
+       }
+
+       /* initialize GPIOs */
+       imx_gpio_mode(PD6_PF_LSCLK);
+       imx_gpio_mode(PD10_PF_SPL_SPR);
+       imx_gpio_mode(PD11_PF_CONTRAST);
+       imx_gpio_mode(PD14_PF_FLM_VSYNC);
+       imx_gpio_mode(PD13_PF_LP_HSYNC);
+       imx_gpio_mode(PD7_PF_REV);
+       imx_gpio_mode(PD8_PF_CLS);
+
+#ifndef CONFIG_MACH_PIMX1
+       /* on PiMX1 used as buffers enable signal
+        */
+       imx_gpio_mode(PD9_PF_PS);
+#endif
+
+#ifndef CONFIG_MACH_MX1FS2
+       /* on mx1fs2 this pin is used to (de)activate the display, so we need
+        * it as a normal gpio
+        */
+       imx_gpio_mode(PD12_PF_ACD_OE);
+#endif
+
+}
+
+#ifdef CONFIG_PM
+/*
+ * Power management hooks.  Note that we won't be called from IRQ context,
+ * unlike the blank functions above, so we may sleep.
+ */
+static int imxfb_suspend(struct device *dev, u32 state, u32 level)
+{
+       struct imxfb_info *fbi = dev_get_drvdata(dev);
+       pr_debug("%s\n",__FUNCTION__);
+
+       if (level == SUSPEND_DISABLE || level == SUSPEND_POWER_DOWN)
+               imxfb_disable_controller(fbi);
+       return 0;
+}
+
+static int imxfb_resume(struct device *dev, u32 level)
+{
+       struct imxfb_info *fbi = dev_get_drvdata(dev);
+       pr_debug("%s\n",__FUNCTION__);
+
+       if (level == RESUME_ENABLE)
+               imxfb_enable_controller(fbi);
+       return 0;
+}
+#else
+#define imxfb_suspend  NULL
+#define imxfb_resume   NULL
+#endif
+
+static int __init imxfb_init_fbinfo(struct device *dev)
+{
+       struct imxfb_mach_info *inf = dev->platform_data;
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct imxfb_info *fbi = info->par;
+
+       pr_debug("%s\n",__FUNCTION__);
+
+       info->pseudo_palette = kmalloc( sizeof(u32) * 16, GFP_KERNEL);
+       if (!info->pseudo_palette)
+               return -ENOMEM;
+
+       memset(fbi, 0, sizeof(struct imxfb_info));
+       fbi->dev = dev;
+
+       strlcpy(info->fix.id, IMX_NAME, sizeof(info->fix.id));
+
+       info->fix.type  = FB_TYPE_PACKED_PIXELS;
+       info->fix.type_aux              = 0;
+       info->fix.xpanstep              = 0;
+       info->fix.ypanstep              = 0;
+       info->fix.ywrapstep             = 0;
+       info->fix.accel = FB_ACCEL_NONE;
+
+       info->var.nonstd                = 0;
+       info->var.activate              = FB_ACTIVATE_NOW;
+       info->var.height                = -1;
+       info->var.width = -1;
+       info->var.accel_flags           = 0;
+       info->var.vmode = FB_VMODE_NONINTERLACED;
+
+       info->fbops                     = &imxfb_ops;
+       info->flags                     = FBINFO_FLAG_DEFAULT;
+       info->pseudo_palette            = (fbi + 1);
+
+       fbi->rgb[RGB_16]                = &def_rgb_16;
+       fbi->rgb[RGB_8]                 = &def_rgb_8;
+
+       fbi->max_xres                   = inf->xres;
+       info->var.xres                  = inf->xres;
+       info->var.xres_virtual          = inf->xres;
+       fbi->max_yres                   = inf->yres;
+       info->var.yres                  = inf->yres;
+       info->var.yres_virtual          = inf->yres;
+       fbi->max_bpp                    = inf->bpp;
+       info->var.bits_per_pixel        = inf->bpp;
+       info->var.pixclock              = inf->pixclock;
+       info->var.hsync_len             = inf->hsync_len;
+       info->var.left_margin           = inf->left_margin;
+       info->var.right_margin          = inf->right_margin;
+       info->var.vsync_len             = inf->vsync_len;
+       info->var.upper_margin          = inf->upper_margin;
+       info->var.lower_margin          = inf->lower_margin;
+       info->var.sync                  = inf->sync;
+       info->var.grayscale             = inf->cmap_greyscale;
+       fbi->cmap_inverse               = inf->cmap_inverse;
+       fbi->pcr                        = inf->pcr;
+       fbi->lscr1                      = inf->lscr1;
+       fbi->pwmr                       = inf->pwmr;
+       fbi->lcd_power                  = inf->lcd_power;
+       fbi->backlight_power            = inf->backlight_power;
+       info->fix.smem_len              = fbi->max_xres * fbi->max_yres *
+                                         fbi->max_bpp / 8;
+
+       return 0;
+}
+
+/*
+ *      Allocates the DRAM memory for the frame buffer.  This buffer is
+ *     remapped into a non-cached, non-buffered, memory region to
+ *      allow pixel writes to occur without flushing the cache.
+ *      Once this area is remapped, all virtual memory access to the
+ *      video memory should occur at the new region.
+ */
+static int __init imxfb_map_video_memory(struct fb_info *info)
+{
+       struct imxfb_info *fbi = info->par;
+
+       fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
+       fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size,
+                                       &fbi->map_dma,GFP_KERNEL);
+
+       if (fbi->map_cpu) {
+               info->screen_base = fbi->map_cpu;
+               fbi->screen_cpu = fbi->map_cpu;
+               fbi->screen_dma = fbi->map_dma;
+               info->fix.smem_start = fbi->screen_dma;
+       }
+
+       return fbi->map_cpu ? 0 : -ENOMEM;
+}
+
+static int __init imxfb_probe(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct imxfb_info *fbi;
+       struct fb_info *info;
+       struct imxfb_mach_info *inf;
+       struct resource *res;
+       int ret;
+
+       printk("i.MX Framebuffer driver\n");
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if(!res)
+               return -ENODEV;
+
+       inf = dev->platform_data;
+       if(!inf) {
+               dev_err(dev,"No platform_data available\n");
+               return -ENOMEM;
+       }
+
+       info = framebuffer_alloc(sizeof(struct imxfb_info), dev);
+       if(!info)
+               return -ENOMEM;
+
+       fbi = info->par;
+
+       dev_set_drvdata(dev, info);
+
+       ret = imxfb_init_fbinfo(dev);
+       if( ret < 0 )
+               goto failed_init;
+
+       res = request_mem_region(res->start, res->end - res->start + 1, "IMXFB");
+       if (!res) {
+               ret = -EBUSY;
+               goto failed_regs;
+       }
+
+       if (!inf->fixed_screen_cpu) {
+               ret = imxfb_map_video_memory(info);
+               if (ret) {
+                       dev_err(dev, "Failed to allocate video RAM: %d\n", ret);
+                       ret = -ENOMEM;
+                       goto failed_map;
+               }
+       } else {
+               /* Fixed framebuffer mapping enables location of the screen in eSRAM */
+               fbi->map_cpu = inf->fixed_screen_cpu;
+               fbi->map_dma = inf->fixed_screen_dma;
+               info->screen_base = fbi->map_cpu;
+               fbi->screen_cpu = fbi->map_cpu;
+               fbi->screen_dma = fbi->map_dma;
+               info->fix.smem_start = fbi->screen_dma;
+       }
+
+       /*
+        * This makes sure that our colour bitfield
+        * descriptors are correctly initialised.
+        */
+       imxfb_check_var(&info->var, info);
+
+       ret = fb_alloc_cmap(&info->cmap, 1<<info->var.bits_per_pixel, 0);
+       if (ret < 0)
+               goto failed_cmap;
+
+       imxfb_setup_gpio(fbi);
+
+       imxfb_set_par(info);
+       ret = register_framebuffer(info);
+       if (ret < 0) {
+               dev_err(dev, "failed to register framebuffer\n");
+               goto failed_register;
+       }
+
+       imxfb_enable_controller(fbi);
+
+       return 0;
+
+failed_register:
+       fb_dealloc_cmap(&info->cmap);
+failed_cmap:
+       if (!inf->fixed_screen_cpu)
+               dma_free_writecombine(dev,fbi->map_size,fbi->map_cpu,
+                          fbi->map_dma);
+failed_map:
+       kfree(info->pseudo_palette);
+failed_regs:
+       release_mem_region(res->start, res->end - res->start);
+failed_init:
+       dev_set_drvdata(dev, NULL);
+       framebuffer_release(info);
+       return ret;
+}
+
+static int imxfb_remove(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct resource *res;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       /* disable LCD controller */
+       LCDC_RMCR &= ~RMCR_LCDC_EN;
+
+       unregister_framebuffer(info);
+
+       fb_dealloc_cmap(&info->cmap);
+       kfree(info->pseudo_palette);
+       framebuffer_release(info);
+
+       release_mem_region(res->start, res->end - res->start + 1);
+       dev_set_drvdata(dev, NULL);
+
+       return 0;
+}
+
+void  imxfb_shutdown(struct device * dev)
+{
+       /* disable LCD Controller */
+       LCDC_RMCR &= ~RMCR_LCDC_EN;
+}
+
+static struct device_driver imxfb_driver = {
+       .name           = "imx-fb",
+       .bus            = &platform_bus_type,
+       .probe          = imxfb_probe,
+       .suspend        = imxfb_suspend,
+       .resume         = imxfb_resume,
+       .remove         = imxfb_remove,
+       .shutdown       = imxfb_shutdown,
+};
+
+int __init imxfb_init(void)
+{
+       return driver_register(&imxfb_driver);
+}
+
+static void __exit imxfb_cleanup(void)
+{
+       driver_unregister(&imxfb_driver);
+}
+
+module_init(imxfb_init);
+module_exit(imxfb_cleanup);
+
+MODULE_DESCRIPTION("Motorola i.MX framebuffer driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/nvidia/Makefile b/drivers/video/nvidia/Makefile
new file mode 100644 (file)
index 0000000..690d37e
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# Makefile for the nVidia framebuffer driver
+#
+
+obj-$(CONFIG_FB_NVIDIA)          += nvidiafb.o
+
+nvidiafb-y                       := nvidia.o nv_hw.o nv_setup.o \
+                                   nv_accel.o
+nvidiafb-$(CONFIG_FB_NVIDIA_I2C) += nv_i2c.o
+nvidiafb-$(CONFIG_PPC_OF)       += nv_of.o
+
+nvidiafb-objs                    := $(nvidiafb-y)
\ No newline at end of file
diff --git a/drivers/video/nvidia/nv_accel.c b/drivers/video/nvidia/nv_accel.c
new file mode 100644 (file)
index 0000000..f377a29
--- /dev/null
@@ -0,0 +1,419 @@
+ /***************************************************************************\
+|*                                                                           *|
+|*       Copyright 1993-2003 NVIDIA, Corporation.  All rights reserved.      *|
+|*                                                                           *|
+|*     NOTICE TO USER:   The source code  is copyrighted under  U.S. and     *|
+|*     international laws.  Users and possessors of this source code are     *|
+|*     hereby granted a nonexclusive,  royalty-free copyright license to     *|
+|*     use this code in individual and commercial software.                  *|
+|*                                                                           *|
+|*     Any use of this source code must include,  in the user documenta-     *|
+|*     tion and  internal comments to the code,  notices to the end user     *|
+|*     as follows:                                                           *|
+|*                                                                           *|
+|*       Copyright 1993-2003 NVIDIA, Corporation.  All rights reserved.      *|
+|*                                                                           *|
+|*     NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY     *|
+|*     OF  THIS SOURCE  CODE  FOR ANY PURPOSE.  IT IS  PROVIDED  "AS IS"     *|
+|*     WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND.  NVIDIA, CORPOR-     *|
+|*     ATION DISCLAIMS ALL WARRANTIES  WITH REGARD  TO THIS SOURCE CODE,     *|
+|*     INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE-     *|
+|*     MENT,  AND FITNESS  FOR A PARTICULAR PURPOSE.   IN NO EVENT SHALL     *|
+|*     NVIDIA, CORPORATION  BE LIABLE FOR ANY SPECIAL,  INDIRECT,  INCI-     *|
+|*     DENTAL, OR CONSEQUENTIAL DAMAGES,  OR ANY DAMAGES  WHATSOEVER RE-     *|
+|*     SULTING FROM LOSS OF USE,  DATA OR PROFITS,  WHETHER IN AN ACTION     *|
+|*     OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,  ARISING OUT OF     *|
+|*     OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE.     *|
+|*                                                                           *|
+|*     U.S. Government  End  Users.   This source code  is a "commercial     *|
+|*     item,"  as that  term is  defined at  48 C.F.R. 2.101 (OCT 1995),     *|
+|*     consisting  of "commercial  computer  software"  and  "commercial     *|
+|*     computer  software  documentation,"  as such  terms  are  used in     *|
+|*     48 C.F.R. 12.212 (SEPT 1995)  and is provided to the U.S. Govern-     *|
+|*     ment only as  a commercial end item.   Consistent with  48 C.F.R.     *|
+|*     12.212 and  48 C.F.R. 227.7202-1 through  227.7202-4 (JUNE 1995),     *|
+|*     all U.S. Government End Users  acquire the source code  with only     *|
+|*     those rights set forth herein.                                        *|
+|*                                                                           *|
+ \***************************************************************************/
+
+/*
+ * GPL Licensing Note - According to Mark Vojkovich, author of the Xorg/
+ * XFree86 'nv' driver, this source code is provided under MIT-style licensing
+ * where the source code is provided "as is" without warranty of any kind.
+ * The only usage restriction is for the copyright notices to be retained
+ * whenever code is used.
+ *
+ * Antonino Daplas <adaplas@pol.net> 2005-03-11
+ */
+
+#include <linux/fb.h>
+#include "nv_type.h"
+#include "nv_proto.h"
+#include "nv_dma.h"
+#include "nv_local.h"
+
+/* There is a HW race condition with videoram command buffers.
+   You can't jump to the location of your put offset.  We write put
+   at the jump offset + SKIPS dwords with noop padding in between
+   to solve this problem */
+#define SKIPS  8
+
+static const int NVCopyROP[16] = {
+       0xCC,                   /* copy   */
+       0x55                    /* invert */
+};
+
+static const int NVCopyROP_PM[16] = {
+       0xCA,                   /* copy  */
+       0x5A,                   /* invert */
+};
+
+static inline void NVFlush(struct nvidia_par *par)
+{
+       int count = 1000000000;
+
+       while (--count && READ_GET(par) != par->dmaPut) ;
+
+       if (!count) {
+               printk("nvidiafb: DMA Flush lockup\n");
+               par->lockup = 1;
+       }
+}
+
+static inline void NVSync(struct nvidia_par *par)
+{
+       int count = 1000000000;
+
+       while (--count && NV_RD32(par->PGRAPH, 0x0700)) ;
+
+       if (!count) {
+               printk("nvidiafb: DMA Sync lockup\n");
+               par->lockup = 1;
+       }
+}
+
+static void NVDmaKickoff(struct nvidia_par *par)
+{
+       if (par->dmaCurrent != par->dmaPut) {
+               par->dmaPut = par->dmaCurrent;
+               WRITE_PUT(par, par->dmaPut);
+       }
+}
+
+static void NVDmaWait(struct nvidia_par *par, int size)
+{
+       int dmaGet;
+       int count = 1000000000, cnt;
+       size++;
+
+       while (par->dmaFree < size && --count && !par->lockup) {
+               dmaGet = READ_GET(par);
+
+               if (par->dmaPut >= dmaGet) {
+                       par->dmaFree = par->dmaMax - par->dmaCurrent;
+                       if (par->dmaFree < size) {
+                               NVDmaNext(par, 0x20000000);
+                               if (dmaGet <= SKIPS) {
+                                       if (par->dmaPut <= SKIPS)
+                                               WRITE_PUT(par, SKIPS + 1);
+                                       cnt = 1000000000;
+                                       do {
+                                               dmaGet = READ_GET(par);
+                                       } while (--cnt && dmaGet <= SKIPS);
+                                       if (!cnt) {
+                                               printk("DMA Get lockup\n");
+                                               par->lockup = 1;
+                                       }
+                               }
+                               WRITE_PUT(par, SKIPS);
+                               par->dmaCurrent = par->dmaPut = SKIPS;
+                               par->dmaFree = dmaGet - (SKIPS + 1);
+                       }
+               } else
+                       par->dmaFree = dmaGet - par->dmaCurrent - 1;
+       }
+
+       if (!count) {
+               printk("DMA Wait Lockup\n");
+               par->lockup = 1;
+       }
+}
+
+static void NVSetPattern(struct nvidia_par *par, u32 clr0, u32 clr1,
+                        u32 pat0, u32 pat1)
+{
+       NVDmaStart(par, PATTERN_COLOR_0, 4);
+       NVDmaNext(par, clr0);
+       NVDmaNext(par, clr1);
+       NVDmaNext(par, pat0);
+       NVDmaNext(par, pat1);
+}
+
+static void NVSetRopSolid(struct nvidia_par *par, u32 rop, u32 planemask)
+{
+       if (planemask != ~0) {
+               NVSetPattern(par, 0, planemask, ~0, ~0);
+               if (par->currentRop != (rop + 32)) {
+                       NVDmaStart(par, ROP_SET, 1);
+                       NVDmaNext(par, NVCopyROP_PM[rop]);
+                       par->currentRop = rop + 32;
+               }
+       } else if (par->currentRop != rop) {
+               if (par->currentRop >= 16)
+                       NVSetPattern(par, ~0, ~0, ~0, ~0);
+               NVDmaStart(par, ROP_SET, 1);
+               NVDmaNext(par, NVCopyROP[rop]);
+               par->currentRop = rop;
+       }
+}
+
+static void NVSetClippingRectangle(struct fb_info *info, int x1, int y1,
+                                  int x2, int y2)
+{
+       struct nvidia_par *par = info->par;
+       int h = y2 - y1 + 1;
+       int w = x2 - x1 + 1;
+
+       NVDmaStart(par, CLIP_POINT, 2);
+       NVDmaNext(par, (y1 << 16) | x1);
+       NVDmaNext(par, (h << 16) | w);
+}
+
+void NVResetGraphics(struct fb_info *info)
+{
+       struct nvidia_par *par = info->par;
+       u32 surfaceFormat, patternFormat, rectFormat, lineFormat;
+       int pitch, i;
+
+       pitch = info->fix.line_length;
+
+       par->dmaBase = (u32 __iomem *) (&par->FbStart[par->FbUsableSize]);
+
+       for (i = 0; i < SKIPS; i++)
+               NV_WR32(&par->dmaBase[i], 0, 0x00000000);
+
+       NV_WR32(&par->dmaBase[0x0 + SKIPS], 0, 0x00040000);
+       NV_WR32(&par->dmaBase[0x1 + SKIPS], 0, 0x80000010);
+       NV_WR32(&par->dmaBase[0x2 + SKIPS], 0, 0x00042000);
+       NV_WR32(&par->dmaBase[0x3 + SKIPS], 0, 0x80000011);
+       NV_WR32(&par->dmaBase[0x4 + SKIPS], 0, 0x00044000);
+       NV_WR32(&par->dmaBase[0x5 + SKIPS], 0, 0x80000012);
+       NV_WR32(&par->dmaBase[0x6 + SKIPS], 0, 0x00046000);
+       NV_WR32(&par->dmaBase[0x7 + SKIPS], 0, 0x80000013);
+       NV_WR32(&par->dmaBase[0x8 + SKIPS], 0, 0x00048000);
+       NV_WR32(&par->dmaBase[0x9 + SKIPS], 0, 0x80000014);
+       NV_WR32(&par->dmaBase[0xA + SKIPS], 0, 0x0004A000);
+       NV_WR32(&par->dmaBase[0xB + SKIPS], 0, 0x80000015);
+       NV_WR32(&par->dmaBase[0xC + SKIPS], 0, 0x0004C000);
+       NV_WR32(&par->dmaBase[0xD + SKIPS], 0, 0x80000016);
+       NV_WR32(&par->dmaBase[0xE + SKIPS], 0, 0x0004E000);
+       NV_WR32(&par->dmaBase[0xF + SKIPS], 0, 0x80000017);
+
+       par->dmaPut = 0;
+       par->dmaCurrent = 16 + SKIPS;
+       par->dmaMax = 8191;
+       par->dmaFree = par->dmaMax - par->dmaCurrent;
+
+       switch (info->var.bits_per_pixel) {
+       case 32:
+       case 24:
+               surfaceFormat = SURFACE_FORMAT_DEPTH24;
+               patternFormat = PATTERN_FORMAT_DEPTH24;
+               rectFormat = RECT_FORMAT_DEPTH24;
+               lineFormat = LINE_FORMAT_DEPTH24;
+               break;
+       case 16:
+               surfaceFormat = SURFACE_FORMAT_DEPTH16;
+               patternFormat = PATTERN_FORMAT_DEPTH16;
+               rectFormat = RECT_FORMAT_DEPTH16;
+               lineFormat = LINE_FORMAT_DEPTH16;
+               break;
+       default:
+               surfaceFormat = SURFACE_FORMAT_DEPTH8;
+               patternFormat = PATTERN_FORMAT_DEPTH8;
+               rectFormat = RECT_FORMAT_DEPTH8;
+               lineFormat = LINE_FORMAT_DEPTH8;
+               break;
+       }
+
+       NVDmaStart(par, SURFACE_FORMAT, 4);
+       NVDmaNext(par, surfaceFormat);
+       NVDmaNext(par, pitch | (pitch << 16));
+       NVDmaNext(par, 0);
+       NVDmaNext(par, 0);
+
+       NVDmaStart(par, PATTERN_FORMAT, 1);
+       NVDmaNext(par, patternFormat);
+
+       NVDmaStart(par, RECT_FORMAT, 1);
+       NVDmaNext(par, rectFormat);
+
+       NVDmaStart(par, LINE_FORMAT, 1);
+       NVDmaNext(par, lineFormat);
+
+       par->currentRop = ~0;   /* set to something invalid */
+       NVSetRopSolid(par, ROP_COPY, ~0);
+
+       NVSetClippingRectangle(info, 0, 0, info->var.xres_virtual,
+                              info->var.yres_virtual);
+
+       NVDmaKickoff(par);
+}
+
+u8 byte_rev[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,
+};
+
+int nvidiafb_sync(struct fb_info *info)
+{
+       struct nvidia_par *par = info->par;
+
+       if (!par->lockup)
+               NVFlush(par);
+
+       if (!par->lockup)
+               NVSync(par);
+
+       return 0;
+}
+
+void nvidiafb_copyarea(struct fb_info *info, const struct fb_copyarea *region)
+{
+       struct nvidia_par *par = info->par;
+
+       if (par->lockup)
+               return cfb_copyarea(info, region);
+
+       NVDmaStart(par, BLIT_POINT_SRC, 3);
+       NVDmaNext(par, (region->sy << 16) | region->sx);
+       NVDmaNext(par, (region->dy << 16) | region->dx);
+       NVDmaNext(par, (region->height << 16) | region->width);
+
+       NVDmaKickoff(par);
+}
+
+void nvidiafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+       struct nvidia_par *par = info->par;
+       u32 color;
+
+       if (par->lockup)
+               return cfb_fillrect(info, rect);
+
+       if (info->var.bits_per_pixel == 8)
+               color = rect->color;
+       else
+               color = ((u32 *) info->pseudo_palette)[rect->color];
+
+       if (rect->rop != ROP_COPY)
+               NVSetRopSolid(par, rect->rop, ~0);
+
+       NVDmaStart(par, RECT_SOLID_COLOR, 1);
+       NVDmaNext(par, color);
+
+       NVDmaStart(par, RECT_SOLID_RECTS(0), 2);
+       NVDmaNext(par, (rect->dx << 16) | rect->dy);
+       NVDmaNext(par, (rect->width << 16) | rect->height);
+
+       NVDmaKickoff(par);
+
+       if (rect->rop != ROP_COPY)
+               NVSetRopSolid(par, ROP_COPY, ~0);
+}
+
+static void nvidiafb_mono_color_expand(struct fb_info *info,
+                                      const struct fb_image *image)
+{
+       struct nvidia_par *par = info->par;
+       u32 fg, bg, mask = ~(~0 >> (32 - info->var.bits_per_pixel));
+       u32 dsize, width, *data = (u32 *) image->data, tmp;
+       int j, k = 0;
+
+       width = (image->width + 31) & ~31;
+       dsize = (width * image->height) >> 5;
+
+       if (info->var.bits_per_pixel == 8) {
+               fg = image->fg_color | mask;
+               bg = image->bg_color | mask;
+       } else {
+               fg = ((u32 *) info->pseudo_palette)[image->fg_color] | mask;
+               bg = ((u32 *) info->pseudo_palette)[image->bg_color] | mask;
+       }
+
+       NVDmaStart(par, RECT_EXPAND_TWO_COLOR_CLIP, 7);
+       NVDmaNext(par, (image->dy << 16) | (image->dx & 0xffff));
+       NVDmaNext(par, ((image->dy + image->height) << 16) |
+                 ((image->dx + image->width) & 0xffff));
+       NVDmaNext(par, bg);
+       NVDmaNext(par, fg);
+       NVDmaNext(par, (image->height << 16) | width);
+       NVDmaNext(par, (image->height << 16) | width);
+       NVDmaNext(par, (image->dy << 16) | (image->dx & 0xffff));
+
+       while (dsize >= RECT_EXPAND_TWO_COLOR_DATA_MAX_DWORDS) {
+               NVDmaStart(par, RECT_EXPAND_TWO_COLOR_DATA(0),
+                          RECT_EXPAND_TWO_COLOR_DATA_MAX_DWORDS);
+
+               for (j = RECT_EXPAND_TWO_COLOR_DATA_MAX_DWORDS; j--;) {
+                       tmp = data[k++];
+                       reverse_order(&tmp);
+                       NVDmaNext(par, tmp);
+               }
+
+               dsize -= RECT_EXPAND_TWO_COLOR_DATA_MAX_DWORDS;
+       }
+
+       if (dsize) {
+               NVDmaStart(par, RECT_EXPAND_TWO_COLOR_DATA(0), dsize);
+
+               for (j = dsize; j--;) {
+                       tmp = data[k++];
+                       reverse_order(&tmp);
+                       NVDmaNext(par, tmp);
+               }
+       }
+
+       NVDmaKickoff(par);
+}
+
+void nvidiafb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+       struct nvidia_par *par = info->par;
+
+       if (image->depth == 1 && !par->lockup)
+               nvidiafb_mono_color_expand(info, image);
+       else
+               cfb_imageblit(info, image);
+}
diff --git a/drivers/video/nvidia/nv_hw.c b/drivers/video/nvidia/nv_hw.c
new file mode 100644 (file)
index 0000000..b989358
--- /dev/null
@@ -0,0 +1,1593 @@
+ /***************************************************************************\
+|*                                                                           *|
+|*       Copyright 1993-2003 NVIDIA, Corporation.  All rights reserved.      *|
+|*                                                                           *|
+|*     NOTICE TO USER:   The source code  is copyrighted under  U.S. and     *|
+|*     international laws.  Users and possessors of this source code are     *|
+|*     hereby granted a nonexclusive,  royalty-free copyright license to     *|
+|*     use this code in individual and commercial software.                  *|
+|*                                                                           *|
+|*     Any use of this source code must include,  in the user documenta-     *|
+|*     tion and  internal comments to the code,  notices to the end user     *|
+|*     as follows:                                                           *|
+|*                                                                           *|
+|*       Copyright 1993-2003 NVIDIA, Corporation.  All rights reserved.      *|
+|*                                                                           *|
+|*     NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY     *|
+|*     OF  THIS SOURCE  CODE  FOR ANY PURPOSE.  IT IS  PROVIDED  "AS IS"     *|
+|*     WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND.  NVIDIA, CORPOR-     *|
+|*     ATION DISCLAIMS ALL WARRANTIES  WITH REGARD  TO THIS SOURCE CODE,     *|
+|*     INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE-     *|
+|*     MENT,  AND FITNESS  FOR A PARTICULAR PURPOSE.   IN NO EVENT SHALL     *|
+|*     NVIDIA, CORPORATION  BE LIABLE FOR ANY SPECIAL,  INDIRECT,  INCI-     *|
+|*     DENTAL, OR CONSEQUENTIAL DAMAGES,  OR ANY DAMAGES  WHATSOEVER RE-     *|
+|*     SULTING FROM LOSS OF USE,  DATA OR PROFITS,  WHETHER IN AN ACTION     *|
+|*     OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,  ARISING OUT OF     *|
+|*     OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE.     *|
+|*                                                                           *|
+|*     U.S. Government  End  Users.   This source code  is a "commercial     *|
+|*     item,"  as that  term is  defined at  48 C.F.R. 2.101 (OCT 1995),     *|
+|*     consisting  of "commercial  computer  software"  and  "commercial     *|
+|*     computer  software  documentation,"  as such  terms  are  used in     *|
+|*     48 C.F.R. 12.212 (SEPT 1995)  and is provided to the U.S. Govern-     *|
+|*     ment only as  a commercial end item.   Consistent with  48 C.F.R.     *|
+|*     12.212 and  48 C.F.R. 227.7202-1 through  227.7202-4 (JUNE 1995),     *|
+|*     all U.S. Government End Users  acquire the source code  with only     *|
+|*     those rights set forth herein.                                        *|
+|*                                                                           *|
+ \***************************************************************************/
+
+/*
+ * GPL Licensing Note - According to Mark Vojkovich, author of the Xorg/
+ * XFree86 'nv' driver, this source code is provided under MIT-style licensing
+ * where the source code is provided "as is" without warranty of any kind.
+ * The only usage restriction is for the copyright notices to be retained
+ * whenever code is used.
+ *
+ * Antonino Daplas <adaplas@pol.net> 2005-03-11
+ */
+
+/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nv/nv_hw.c,v 1.4 2003/11/03 05:11:25 tsi Exp $ */
+
+#include <linux/pci.h>
+#include "nv_type.h"
+#include "nv_local.h"
+
+void NVLockUnlock(struct nvidia_par *par, int Lock)
+{
+       u8 cr11;
+
+       VGA_WR08(par->PCIO, 0x3D4, 0x1F);
+       VGA_WR08(par->PCIO, 0x3D5, Lock ? 0x99 : 0x57);
+
+       VGA_WR08(par->PCIO, 0x3D4, 0x11);
+       cr11 = VGA_RD08(par->PCIO, 0x3D5);
+       if (Lock)
+               cr11 |= 0x80;
+       else
+               cr11 &= ~0x80;
+       VGA_WR08(par->PCIO, 0x3D5, cr11);
+}
+
+int NVShowHideCursor(struct nvidia_par *par, int ShowHide)
+{
+       int cur = par->CurrentState->cursor1;
+
+       par->CurrentState->cursor1 = (par->CurrentState->cursor1 & 0xFE) |
+           (ShowHide & 0x01);
+       VGA_WR08(par->PCIO, 0x3D4, 0x31);
+       VGA_WR08(par->PCIO, 0x3D5, par->CurrentState->cursor1);
+
+       if (par->Architecture == NV_ARCH_40)
+               NV_WR32(par->PRAMDAC, 0x0300, NV_RD32(par->PRAMDAC, 0x0300));
+
+       return (cur & 0x01);
+}
+
+/****************************************************************************\
+*                                                                            *
+* The video arbitration routines calculate some "magic" numbers.  Fixes      *
+* the snow seen when accessing the framebuffer without it.                   *
+* It just works (I hope).                                                    *
+*                                                                            *
+\****************************************************************************/
+
+typedef struct {
+       int graphics_lwm;
+       int video_lwm;
+       int graphics_burst_size;
+       int video_burst_size;
+       int valid;
+} nv4_fifo_info;
+
+typedef struct {
+       int pclk_khz;
+       int mclk_khz;
+       int nvclk_khz;
+       char mem_page_miss;
+       char mem_latency;
+       int memory_width;
+       char enable_video;
+       char gr_during_vid;
+       char pix_bpp;
+       char mem_aligned;
+       char enable_mp;
+} nv4_sim_state;
+
+typedef struct {
+       int graphics_lwm;
+       int video_lwm;
+       int graphics_burst_size;
+       int video_burst_size;
+       int valid;
+} nv10_fifo_info;
+
+typedef struct {
+       int pclk_khz;
+       int mclk_khz;
+       int nvclk_khz;
+       char mem_page_miss;
+       char mem_latency;
+       int memory_type;
+       int memory_width;
+       char enable_video;
+       char gr_during_vid;
+       char pix_bpp;
+       char mem_aligned;
+       char enable_mp;
+} nv10_sim_state;
+
+static void nvGetClocks(struct nvidia_par *par, unsigned int *MClk,
+                       unsigned int *NVClk)
+{
+       unsigned int pll, N, M, MB, NB, P;
+
+       if (par->Architecture >= NV_ARCH_40) {
+               pll = NV_RD32(par->PMC, 0x4020);
+               P = (pll >> 16) & 0x03;
+               pll = NV_RD32(par->PMC, 0x4024);
+               M = pll & 0xFF;
+               N = (pll >> 8) & 0xFF;
+               MB = (pll >> 16) & 0xFF;
+               NB = (pll >> 24) & 0xFF;
+               *MClk = ((N * NB * par->CrystalFreqKHz) / (M * MB)) >> P;
+
+               pll = NV_RD32(par->PMC, 0x4000);
+               P = (pll >> 16) & 0x03;
+               pll = NV_RD32(par->PMC, 0x4004);
+               M = pll & 0xFF;
+               N = (pll >> 8) & 0xFF;
+               MB = (pll >> 16) & 0xFF;
+               NB = (pll >> 24) & 0xFF;
+
+               *NVClk = ((N * NB * par->CrystalFreqKHz) / (M * MB)) >> P;
+       } else if (par->twoStagePLL) {
+               pll = NV_RD32(par->PRAMDAC0, 0x0504);
+               M = pll & 0xFF;
+               N = (pll >> 8) & 0xFF;
+               P = (pll >> 16) & 0x0F;
+               pll = NV_RD32(par->PRAMDAC0, 0x0574);
+               if (pll & 0x80000000) {
+                       MB = pll & 0xFF;
+                       NB = (pll >> 8) & 0xFF;
+               } else {
+                       MB = 1;
+                       NB = 1;
+               }
+               *MClk = ((N * NB * par->CrystalFreqKHz) / (M * MB)) >> P;
+
+               pll = NV_RD32(par->PRAMDAC0, 0x0500);
+               M = pll & 0xFF;
+               N = (pll >> 8) & 0xFF;
+               P = (pll >> 16) & 0x0F;
+               pll = NV_RD32(par->PRAMDAC0, 0x0570);
+               if (pll & 0x80000000) {
+                       MB = pll & 0xFF;
+                       NB = (pll >> 8) & 0xFF;
+               } else {
+                       MB = 1;
+                       NB = 1;
+               }
+               *NVClk = ((N * NB * par->CrystalFreqKHz) / (M * MB)) >> P;
+       } else
+           if (((par->Chipset & 0x0ff0) == 0x0300) ||
+               ((par->Chipset & 0x0ff0) == 0x0330)) {
+               pll = NV_RD32(par->PRAMDAC0, 0x0504);
+               M = pll & 0x0F;
+               N = (pll >> 8) & 0xFF;
+               P = (pll >> 16) & 0x07;
+               if (pll & 0x00000080) {
+                       MB = (pll >> 4) & 0x07;
+                       NB = (pll >> 19) & 0x1f;
+               } else {
+                       MB = 1;
+                       NB = 1;
+               }
+               *MClk = ((N * NB * par->CrystalFreqKHz) / (M * MB)) >> P;
+
+               pll = NV_RD32(par->PRAMDAC0, 0x0500);
+               M = pll & 0x0F;
+               N = (pll >> 8) & 0xFF;
+               P = (pll >> 16) & 0x07;
+               if (pll & 0x00000080) {
+                       MB = (pll >> 4) & 0x07;
+                       NB = (pll >> 19) & 0x1f;
+               } else {
+                       MB = 1;
+                       NB = 1;
+               }
+               *NVClk = ((N * NB * par->CrystalFreqKHz) / (M * MB)) >> P;
+       } else {
+               pll = NV_RD32(par->PRAMDAC0, 0x0504);
+               M = pll & 0xFF;
+               N = (pll >> 8) & 0xFF;
+               P = (pll >> 16) & 0x0F;
+               *MClk = (N * par->CrystalFreqKHz / M) >> P;
+
+               pll = NV_RD32(par->PRAMDAC0, 0x0500);
+               M = pll & 0xFF;
+               N = (pll >> 8) & 0xFF;
+               P = (pll >> 16) & 0x0F;
+               *NVClk = (N * par->CrystalFreqKHz / M) >> P;
+       }
+}
+
+static void nv4CalcArbitration(nv4_fifo_info * fifo, nv4_sim_state * arb)
+{
+       int data, pagemiss, cas, width, video_enable, bpp;
+       int nvclks, mclks, pclks, vpagemiss, crtpagemiss, vbs;
+       int found, mclk_extra, mclk_loop, cbs, m1, p1;
+       int mclk_freq, pclk_freq, nvclk_freq, mp_enable;
+       int us_m, us_n, us_p, video_drain_rate, crtc_drain_rate;
+       int vpm_us, us_video, vlwm, video_fill_us, cpm_us, us_crt, clwm;
+
+       fifo->valid = 1;
+       pclk_freq = arb->pclk_khz;
+       mclk_freq = arb->mclk_khz;
+       nvclk_freq = arb->nvclk_khz;
+       pagemiss = arb->mem_page_miss;
+       cas = arb->mem_latency;
+       width = arb->memory_width >> 6;
+       video_enable = arb->enable_video;
+       bpp = arb->pix_bpp;
+       mp_enable = arb->enable_mp;
+       clwm = 0;
+       vlwm = 0;
+       cbs = 128;
+       pclks = 2;
+       nvclks = 2;
+       nvclks += 2;
+       nvclks += 1;
+       mclks = 5;
+       mclks += 3;
+       mclks += 1;
+       mclks += cas;
+       mclks += 1;
+       mclks += 1;
+       mclks += 1;
+       mclks += 1;
+       mclk_extra = 3;
+       nvclks += 2;
+       nvclks += 1;
+       nvclks += 1;
+       nvclks += 1;
+       if (mp_enable)
+               mclks += 4;
+       nvclks += 0;
+       pclks += 0;
+       found = 0;
+       vbs = 0;
+       while (found != 1) {
+               fifo->valid = 1;
+               found = 1;
+               mclk_loop = mclks + mclk_extra;
+               us_m = mclk_loop * 1000 * 1000 / mclk_freq;
+               us_n = nvclks * 1000 * 1000 / nvclk_freq;
+               us_p = nvclks * 1000 * 1000 / pclk_freq;
+               if (video_enable) {
+                       video_drain_rate = pclk_freq * 2;
+                       crtc_drain_rate = pclk_freq * bpp / 8;
+                       vpagemiss = 2;
+                       vpagemiss += 1;
+                       crtpagemiss = 2;
+                       vpm_us =
+                           (vpagemiss * pagemiss) * 1000 * 1000 / mclk_freq;
+                       if (nvclk_freq * 2 > mclk_freq * width)
+                               video_fill_us =
+                                   cbs * 1000 * 1000 / 16 / nvclk_freq;
+                       else
+                               video_fill_us =
+                                   cbs * 1000 * 1000 / (8 * width) /
+                                   mclk_freq;
+                       us_video = vpm_us + us_m + us_n + us_p + video_fill_us;
+                       vlwm = us_video * video_drain_rate / (1000 * 1000);
+                       vlwm++;
+                       vbs = 128;
+                       if (vlwm > 128)
+                               vbs = 64;
+                       if (vlwm > (256 - 64))
+                               vbs = 32;
+                       if (nvclk_freq * 2 > mclk_freq * width)
+                               video_fill_us =
+                                   vbs * 1000 * 1000 / 16 / nvclk_freq;
+                       else
+                               video_fill_us =
+                                   vbs * 1000 * 1000 / (8 * width) /
+                                   mclk_freq;
+                       cpm_us =
+                           crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq;
+                       us_crt =
+                           us_video + video_fill_us + cpm_us + us_m + us_n +
+                           us_p;
+                       clwm = us_crt * crtc_drain_rate / (1000 * 1000);
+                       clwm++;
+               } else {
+                       crtc_drain_rate = pclk_freq * bpp / 8;
+                       crtpagemiss = 2;
+                       crtpagemiss += 1;
+                       cpm_us =
+                           crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq;
+                       us_crt = cpm_us + us_m + us_n + us_p;
+                       clwm = us_crt * crtc_drain_rate / (1000 * 1000);
+                       clwm++;
+               }
+               m1 = clwm + cbs - 512;
+               p1 = m1 * pclk_freq / mclk_freq;
+               p1 = p1 * bpp / 8;
+               if ((p1 < m1) && (m1 > 0)) {
+                       fifo->valid = 0;
+                       found = 0;
+                       if (mclk_extra == 0)
+                               found = 1;
+                       mclk_extra--;
+               } else if (video_enable) {
+                       if ((clwm > 511) || (vlwm > 255)) {
+                               fifo->valid = 0;
+                               found = 0;
+                               if (mclk_extra == 0)
+                                       found = 1;
+                               mclk_extra--;
+                       }
+               } else {
+                       if (clwm > 519) {
+                               fifo->valid = 0;
+                               found = 0;
+                               if (mclk_extra == 0)
+                                       found = 1;
+                               mclk_extra--;
+                       }
+               }
+               if (clwm < 384)
+                       clwm = 384;
+               if (vlwm < 128)
+                       vlwm = 128;
+               data = (int)(clwm);
+               fifo->graphics_lwm = data;
+               fifo->graphics_burst_size = 128;
+               data = (int)((vlwm + 15));
+               fifo->video_lwm = data;
+               fifo->video_burst_size = vbs;
+       }
+}
+
+static void nv4UpdateArbitrationSettings(unsigned VClk,
+                                        unsigned pixelDepth,
+                                        unsigned *burst,
+                                        unsigned *lwm, struct nvidia_par *par)
+{
+       nv4_fifo_info fifo_data;
+       nv4_sim_state sim_data;
+       unsigned int MClk, NVClk, cfg1;
+
+       nvGetClocks(par, &MClk, &NVClk);
+
+       cfg1 = NV_RD32(par->PFB, 0x00000204);
+       sim_data.pix_bpp = (char)pixelDepth;
+       sim_data.enable_video = 0;
+       sim_data.enable_mp = 0;
+       sim_data.memory_width = (NV_RD32(par->PEXTDEV, 0x0000) & 0x10) ?
+           128 : 64;
+       sim_data.mem_latency = (char)cfg1 & 0x0F;
+       sim_data.mem_aligned = 1;
+       sim_data.mem_page_miss =
+           (char)(((cfg1 >> 4) & 0x0F) + ((cfg1 >> 31) & 0x01));
+       sim_data.gr_during_vid = 0;
+       sim_data.pclk_khz = VClk;
+       sim_data.mclk_khz = MClk;
+       sim_data.nvclk_khz = NVClk;
+       nv4CalcArbitration(&fifo_data, &sim_data);
+       if (fifo_data.valid) {
+               int b = fifo_data.graphics_burst_size >> 4;
+               *burst = 0;
+               while (b >>= 1)
+                       (*burst)++;
+               *lwm = fifo_data.graphics_lwm >> 3;
+       }
+}
+
+static void nv10CalcArbitration(nv10_fifo_info * fifo, nv10_sim_state * arb)
+{
+       int data, pagemiss, width, video_enable, bpp;
+       int nvclks, mclks, pclks, vpagemiss, crtpagemiss;
+       int nvclk_fill;
+       int found, mclk_extra, mclk_loop, cbs, m1;
+       int mclk_freq, pclk_freq, nvclk_freq, mp_enable;
+       int us_m, us_m_min, us_n, us_p, crtc_drain_rate;
+       int vus_m;
+       int vpm_us, us_video, cpm_us, us_crt, clwm;
+       int clwm_rnd_down;
+       int m2us, us_pipe_min, p1clk, p2;
+       int min_mclk_extra;
+       int us_min_mclk_extra;
+
+       fifo->valid = 1;
+       pclk_freq = arb->pclk_khz;      /* freq in KHz */
+       mclk_freq = arb->mclk_khz;
+       nvclk_freq = arb->nvclk_khz;
+       pagemiss = arb->mem_page_miss;
+       width = arb->memory_width / 64;
+       video_enable = arb->enable_video;
+       bpp = arb->pix_bpp;
+       mp_enable = arb->enable_mp;
+       clwm = 0;
+
+       cbs = 512;
+
+       pclks = 4;      /* lwm detect. */
+
+       nvclks = 3;     /* lwm -> sync. */
+       nvclks += 2;    /* fbi bus cycles (1 req + 1 busy) */
+       /* 2 edge sync.  may be very close to edge so just put one. */
+       mclks = 1;
+       mclks += 1;     /* arb_hp_req */
+       mclks += 5;     /* ap_hp_req   tiling pipeline */
+
+       mclks += 2;     /* tc_req     latency fifo */
+       mclks += 2;     /* fb_cas_n_  memory request to fbio block */
+       mclks += 7;     /* sm_d_rdv   data returned from fbio block */
+
+       /* fb.rd.d.Put_gc   need to accumulate 256 bits for read */
+       if (arb->memory_type == 0)
+               if (arb->memory_width == 64)    /* 64 bit bus */
+                       mclks += 4;
+               else
+                       mclks += 2;
+       else if (arb->memory_width == 64)       /* 64 bit bus */
+               mclks += 2;
+       else
+               mclks += 1;
+
+       if ((!video_enable) && (arb->memory_width == 128)) {
+               mclk_extra = (bpp == 32) ? 31 : 42;     /* Margin of error */
+               min_mclk_extra = 17;
+       } else {
+               mclk_extra = (bpp == 32) ? 8 : 4;       /* Margin of error */
+               /* mclk_extra = 4; *//* Margin of error */
+               min_mclk_extra = 18;
+       }
+
+       /* 2 edge sync.  may be very close to edge so just put one. */
+       nvclks += 1;
+       nvclks += 1;            /* fbi_d_rdv_n */
+       nvclks += 1;            /* Fbi_d_rdata */
+       nvclks += 1;            /* crtfifo load */
+
+       if (mp_enable)
+               mclks += 4;     /* Mp can get in with a burst of 8. */
+       /* Extra clocks determined by heuristics */
+
+       nvclks += 0;
+       pclks += 0;
+       found = 0;
+       while (found != 1) {
+               fifo->valid = 1;
+               found = 1;
+               mclk_loop = mclks + mclk_extra;
+               /* Mclk latency in us */
+               us_m = mclk_loop * 1000 * 1000 / mclk_freq;
+               /* Minimum Mclk latency in us */
+               us_m_min = mclks * 1000 * 1000 / mclk_freq;
+               us_min_mclk_extra = min_mclk_extra * 1000 * 1000 / mclk_freq;
+               /* nvclk latency in us */
+               us_n = nvclks * 1000 * 1000 / nvclk_freq;
+               /* nvclk latency in us */
+               us_p = pclks * 1000 * 1000 / pclk_freq;
+               us_pipe_min = us_m_min + us_n + us_p;
+
+               /* Mclk latency in us */
+               vus_m = mclk_loop * 1000 * 1000 / mclk_freq;
+
+               if (video_enable) {
+                       crtc_drain_rate = pclk_freq * bpp / 8;  /* MB/s */
+
+                       vpagemiss = 1;  /* self generating page miss */
+                       vpagemiss += 1; /* One higher priority before */
+
+                       crtpagemiss = 2;        /* self generating page miss */
+                       if (mp_enable)
+                               crtpagemiss += 1;       /* if MA0 conflict */
+
+                       vpm_us =
+                           (vpagemiss * pagemiss) * 1000 * 1000 / mclk_freq;
+
+                       /* Video has separate read return path */
+                       us_video = vpm_us + vus_m;
+
+                       cpm_us =
+                           crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq;
+                       /* Wait for video */
+                       us_crt = us_video
+                           + cpm_us    /* CRT Page miss */
+                           + us_m + us_n + us_p        /* other latency */
+                           ;
+
+                       clwm = us_crt * crtc_drain_rate / (1000 * 1000);
+                       /* fixed point <= float_point - 1.  Fixes that */
+                       clwm++;
+               } else {
+                   /* bpp * pclk/8 */
+                       crtc_drain_rate = pclk_freq * bpp / 8;
+
+                       crtpagemiss = 1;        /* self generating page miss */
+                       crtpagemiss += 1;       /* MA0 page miss */
+                       if (mp_enable)
+                               crtpagemiss += 1;       /* if MA0 conflict */
+                       cpm_us =
+                           crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq;
+                       us_crt = cpm_us + us_m + us_n + us_p;
+                       clwm = us_crt * crtc_drain_rate / (1000 * 1000);
+                       /* fixed point <= float_point - 1.  Fixes that */
+                       clwm++;
+
+                       /* Finally, a heuristic check when width == 64 bits */
+                       if (width == 1) {
+                               nvclk_fill = nvclk_freq * 8;
+                               if (crtc_drain_rate * 100 >= nvclk_fill * 102)
+                                       /*Large number to fail */
+                                       clwm = 0xfff;
+
+                               else if (crtc_drain_rate * 100 >=
+                                        nvclk_fill * 98) {
+                                       clwm = 1024;
+                                       cbs = 512;
+                               }
+                       }
+               }
+
+               /*
+                  Overfill check:
+                */
+
+               clwm_rnd_down = ((int)clwm / 8) * 8;
+               if (clwm_rnd_down < clwm)
+                       clwm += 8;
+
+               m1 = clwm + cbs - 1024; /* Amount of overfill */
+               m2us = us_pipe_min + us_min_mclk_extra;
+
+               /* pclk cycles to drain */
+               p1clk = m2us * pclk_freq / (1000 * 1000);
+               p2 = p1clk * bpp / 8;   /* bytes drained. */
+
+               if ((p2 < m1) && (m1 > 0)) {
+                       fifo->valid = 0;
+                       found = 0;
+                       if (min_mclk_extra == 0) {
+                               if (cbs <= 32) {
+                                       /* Can't adjust anymore! */
+                                       found = 1;
+                               } else {
+                                       /* reduce the burst size */
+                                       cbs = cbs / 2;
+                               }
+                       } else {
+                               min_mclk_extra--;
+                       }
+               } else {
+                       if (clwm > 1023) {      /* Have some margin */
+                               fifo->valid = 0;
+                               found = 0;
+                               if (min_mclk_extra == 0)
+                                       /* Can't adjust anymore! */
+                                       found = 1;
+                               else
+                                       min_mclk_extra--;
+                       }
+               }
+
+               if (clwm < (1024 - cbs + 8))
+                       clwm = 1024 - cbs + 8;
+               data = (int)(clwm);
+               /*  printf("CRT LWM: %f bytes, prog: 0x%x, bs: 256\n",
+                   clwm, data ); */
+               fifo->graphics_lwm = data;
+               fifo->graphics_burst_size = cbs;
+
+               fifo->video_lwm = 1024;
+               fifo->video_burst_size = 512;
+       }
+}
+
+static void nv10UpdateArbitrationSettings(unsigned VClk,
+                                         unsigned pixelDepth,
+                                         unsigned *burst,
+                                         unsigned *lwm,
+                                         struct nvidia_par *par)
+{
+       nv10_fifo_info fifo_data;
+       nv10_sim_state sim_data;
+       unsigned int MClk, NVClk, cfg1;
+
+       nvGetClocks(par, &MClk, &NVClk);
+
+       cfg1 = NV_RD32(par->PFB, 0x0204);
+       sim_data.pix_bpp = (char)pixelDepth;
+       sim_data.enable_video = 1;
+       sim_data.enable_mp = 0;
+       sim_data.memory_type = (NV_RD32(par->PFB, 0x0200) & 0x01) ? 1 : 0;
+       sim_data.memory_width = (NV_RD32(par->PEXTDEV, 0x0000) & 0x10) ?
+           128 : 64;
+       sim_data.mem_latency = (char)cfg1 & 0x0F;
+       sim_data.mem_aligned = 1;
+       sim_data.mem_page_miss =
+           (char)(((cfg1 >> 4) & 0x0F) + ((cfg1 >> 31) & 0x01));
+       sim_data.gr_during_vid = 0;
+       sim_data.pclk_khz = VClk;
+       sim_data.mclk_khz = MClk;
+       sim_data.nvclk_khz = NVClk;
+       nv10CalcArbitration(&fifo_data, &sim_data);
+       if (fifo_data.valid) {
+               int b = fifo_data.graphics_burst_size >> 4;
+               *burst = 0;
+               while (b >>= 1)
+                       (*burst)++;
+               *lwm = fifo_data.graphics_lwm >> 3;
+       }
+}
+
+static void nv30UpdateArbitrationSettings (
+    struct nvidia_par *par,
+    unsigned int      *burst,
+    unsigned int      *lwm
+)
+{
+    unsigned int MClk, NVClk;
+    unsigned int fifo_size, burst_size, graphics_lwm;
+
+    fifo_size = 2048;
+    burst_size = 512;
+    graphics_lwm = fifo_size - burst_size;
+
+    nvGetClocks(par, &MClk, &NVClk);
+
+    *burst = 0;
+    burst_size >>= 5;
+    while(burst_size >>= 1) (*burst)++;
+    *lwm = graphics_lwm >> 3;
+}
+
+static void nForceUpdateArbitrationSettings(unsigned VClk,
+                                           unsigned pixelDepth,
+                                           unsigned *burst,
+                                           unsigned *lwm,
+                                           struct nvidia_par *par)
+{
+       nv10_fifo_info fifo_data;
+       nv10_sim_state sim_data;
+       unsigned int M, N, P, pll, MClk, NVClk, memctrl;
+       struct pci_dev *dev;
+
+       if ((par->Chipset & 0x0FF0) == 0x01A0) {
+               unsigned int uMClkPostDiv;
+               dev = pci_find_slot(0, 3);
+               pci_read_config_dword(dev, 0x6C, &uMClkPostDiv);
+               uMClkPostDiv = (uMClkPostDiv >> 8) & 0xf;
+
+               if (!uMClkPostDiv)
+                       uMClkPostDiv = 4;
+               MClk = 400000 / uMClkPostDiv;
+       } else {
+               dev = pci_find_slot(0, 5);
+               pci_read_config_dword(dev, 0x4c, &MClk);
+               MClk /= 1000;
+       }
+
+       pll = NV_RD32(par->PRAMDAC0, 0x0500);
+       M = (pll >> 0) & 0xFF;
+       N = (pll >> 8) & 0xFF;
+       P = (pll >> 16) & 0x0F;
+       NVClk = (N * par->CrystalFreqKHz / M) >> P;
+       sim_data.pix_bpp = (char)pixelDepth;
+       sim_data.enable_video = 0;
+       sim_data.enable_mp = 0;
+       pci_find_slot(0, 1);
+       pci_read_config_dword(dev, 0x7C, &sim_data.memory_type);
+       sim_data.memory_type = (sim_data.memory_type >> 12) & 1;
+       sim_data.memory_width = 64;
+
+       dev = pci_find_slot(0, 3);
+       pci_read_config_dword(dev, 0, &memctrl);
+       memctrl >>= 16;
+
+       if ((memctrl == 0x1A9) || (memctrl == 0x1AB) || (memctrl == 0x1ED)) {
+               int dimm[3];
+
+               pci_find_slot(0, 2);
+               pci_read_config_dword(dev, 0x40, &dimm[0]);
+               dimm[0] = (dimm[0] >> 8) & 0x4f;
+               pci_read_config_dword(dev, 0x44, &dimm[1]);
+               dimm[1] = (dimm[1] >> 8) & 0x4f;
+               pci_read_config_dword(dev, 0x48, &dimm[2]);
+               dimm[2] = (dimm[2] >> 8) & 0x4f;
+
+               if ((dimm[0] + dimm[1]) != dimm[2]) {
+                       printk("nvidiafb: your nForce DIMMs are not arranged "
+                              "in optimal banks!\n");
+               }
+       }
+
+       sim_data.mem_latency = 3;
+       sim_data.mem_aligned = 1;
+       sim_data.mem_page_miss = 10;
+       sim_data.gr_during_vid = 0;
+       sim_data.pclk_khz = VClk;
+       sim_data.mclk_khz = MClk;
+       sim_data.nvclk_khz = NVClk;
+       nv10CalcArbitration(&fifo_data, &sim_data);
+       if (fifo_data.valid) {
+               int b = fifo_data.graphics_burst_size >> 4;
+               *burst = 0;
+               while (b >>= 1)
+                       (*burst)++;
+               *lwm = fifo_data.graphics_lwm >> 3;
+       }
+}
+
+/****************************************************************************\
+*                                                                            *
+*                          RIVA Mode State Routines                          *
+*                                                                            *
+\****************************************************************************/
+
+/*
+ * Calculate the Video Clock parameters for the PLL.
+ */
+static void CalcVClock(int clockIn,
+                      int *clockOut, u32 * pllOut, struct nvidia_par *par)
+{
+       unsigned lowM, highM;
+       unsigned DeltaNew, DeltaOld;
+       unsigned VClk, Freq;
+       unsigned M, N, P;
+
+       DeltaOld = 0xFFFFFFFF;
+
+       VClk = (unsigned)clockIn;
+
+       if (par->CrystalFreqKHz == 13500) {
+               lowM = 7;
+               highM = 13;
+       } else {
+               lowM = 8;
+               highM = 14;
+       }
+
+       for (P = 0; P <= 4; P++) {
+               Freq = VClk << P;
+               if ((Freq >= 128000) && (Freq <= 350000)) {
+                       for (M = lowM; M <= highM; M++) {
+                               N = ((VClk << P) * M) / par->CrystalFreqKHz;
+                               if (N <= 255) {
+                                       Freq =
+                                           ((par->CrystalFreqKHz * N) /
+                                            M) >> P;
+                                       if (Freq > VClk)
+                                               DeltaNew = Freq - VClk;
+                                       else
+                                               DeltaNew = VClk - Freq;
+                                       if (DeltaNew < DeltaOld) {
+                                               *pllOut =
+                                                   (P << 16) | (N << 8) | M;
+                                               *clockOut = Freq;
+                                               DeltaOld = DeltaNew;
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+static void CalcVClock2Stage(int clockIn,
+                            int *clockOut,
+                            u32 * pllOut,
+                            u32 * pllBOut, struct nvidia_par *par)
+{
+       unsigned DeltaNew, DeltaOld;
+       unsigned VClk, Freq;
+       unsigned M, N, P;
+
+       DeltaOld = 0xFFFFFFFF;
+
+       *pllBOut = 0x80000401;  /* fixed at x4 for now */
+
+       VClk = (unsigned)clockIn;
+
+       for (P = 0; P <= 6; P++) {
+               Freq = VClk << P;
+               if ((Freq >= 400000) && (Freq <= 1000000)) {
+                       for (M = 1; M <= 13; M++) {
+                               N = ((VClk << P) * M) /
+                                   (par->CrystalFreqKHz << 2);
+                               if ((N >= 5) && (N <= 255)) {
+                                       Freq =
+                                           (((par->CrystalFreqKHz << 2) * N) /
+                                            M) >> P;
+                                       if (Freq > VClk)
+                                               DeltaNew = Freq - VClk;
+                                       else
+                                               DeltaNew = VClk - Freq;
+                                       if (DeltaNew < DeltaOld) {
+                                               *pllOut =
+                                                   (P << 16) | (N << 8) | M;
+                                               *clockOut = Freq;
+                                               DeltaOld = DeltaNew;
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+/*
+ * Calculate extended mode parameters (SVGA) and save in a
+ * mode state structure.
+ */
+void NVCalcStateExt(struct nvidia_par *par,
+                   RIVA_HW_STATE * state,
+                   int bpp,
+                   int width,
+                   int hDisplaySize, int height, int dotClock, int flags)
+{
+       int pixelDepth, VClk;
+       /*
+        * Save mode parameters.
+        */
+       state->bpp = bpp;       /* this is not bitsPerPixel, it's 8,15,16,32 */
+       state->width = width;
+       state->height = height;
+       /*
+        * Extended RIVA registers.
+        */
+       pixelDepth = (bpp + 1) / 8;
+       if (par->twoStagePLL)
+               CalcVClock2Stage(dotClock, &VClk, &state->pll, &state->pllB,
+                                par);
+       else
+               CalcVClock(dotClock, &VClk, &state->pll, par);
+
+       switch (par->Architecture) {
+       case NV_ARCH_04:
+               nv4UpdateArbitrationSettings(VClk,
+                                            pixelDepth * 8,
+                                            &(state->arbitration0),
+                                            &(state->arbitration1), par);
+               state->cursor0 = 0x00;
+               state->cursor1 = 0xbC;
+               if (flags & FB_VMODE_DOUBLE)
+                       state->cursor1 |= 2;
+               state->cursor2 = 0x00000000;
+               state->pllsel = 0x10000700;
+               state->config = 0x00001114;
+               state->general = bpp == 16 ? 0x00101100 : 0x00100100;
+               state->repaint1 = hDisplaySize < 1280 ? 0x04 : 0x00;
+               break;
+       case NV_ARCH_10:
+       case NV_ARCH_20:
+       case NV_ARCH_30:
+       default:
+               if (((par->Chipset & 0xffff) == 0x01A0) ||
+                   ((par->Chipset & 0xffff) == 0x01f0)) {
+                       nForceUpdateArbitrationSettings(VClk,
+                                                       pixelDepth * 8,
+                                                       &(state->arbitration0),
+                                                       &(state->arbitration1),
+                                                       par);
+               } else if (par->Architecture < NV_ARCH_30) {
+                       nv10UpdateArbitrationSettings(VClk,
+                                                     pixelDepth * 8,
+                                                     &(state->arbitration0),
+                                                     &(state->arbitration1),
+                                                     par);
+               } else {
+                       nv30UpdateArbitrationSettings(par,
+                                                     &(state->arbitration0),
+                                                     &(state->arbitration1));
+               }
+
+               state->cursor0 = 0x80 | (par->CursorStart >> 17);
+               state->cursor1 = (par->CursorStart >> 11) << 2;
+               state->cursor2 = par->CursorStart >> 24;
+               if (flags & FB_VMODE_DOUBLE)
+                       state->cursor1 |= 2;
+               state->pllsel = 0x10000700;
+               state->config = NV_RD32(par->PFB, 0x00000200);
+               state->general = bpp == 16 ? 0x00101100 : 0x00100100;
+               state->repaint1 = hDisplaySize < 1280 ? 0x04 : 0x00;
+               break;
+       }
+
+       if (bpp != 8)           /* DirectColor */
+               state->general |= 0x00000030;
+
+       state->repaint0 = (((width / 8) * pixelDepth) & 0x700) >> 3;
+       state->pixel = (pixelDepth > 2) ? 3 : pixelDepth;
+}
+
+void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state)
+{
+       int i;
+
+       NV_WR32(par->PMC, 0x0140, 0x00000000);
+       NV_WR32(par->PMC, 0x0200, 0xFFFF00FF);
+       NV_WR32(par->PMC, 0x0200, 0xFFFFFFFF);
+
+       NV_WR32(par->PTIMER, 0x0200 * 4, 0x00000008);
+       NV_WR32(par->PTIMER, 0x0210 * 4, 0x00000003);
+       NV_WR32(par->PTIMER, 0x0140 * 4, 0x00000000);
+       NV_WR32(par->PTIMER, 0x0100 * 4, 0xFFFFFFFF);
+
+       if (par->Architecture == NV_ARCH_04) {
+               NV_WR32(par->PFB, 0x0200, state->config);
+       } else if ((par->Chipset & 0xfff0) == 0x0090) {
+               for (i = 0; i < 15; i++) {
+                       NV_WR32(par->PFB, 0x0600 + (i * 0x10), 0);
+                       NV_WR32(par->PFB, 0x0604 + (i * 0x10), par->FbMapSize - 1);
+               }
+       } else {
+               for (i = 0; i < 8; i++) {
+                       NV_WR32(par->PFB, 0x0240 + (i * 0x10), 0);
+                       NV_WR32(par->PFB, 0x0244 + (i * 0x10), par->FbMapSize - 1);
+               }
+       }
+
+       if (par->Architecture >= NV_ARCH_40) {
+               NV_WR32(par->PRAMIN, 0x0000 * 4, 0x80000010);
+               NV_WR32(par->PRAMIN, 0x0001 * 4, 0x00101202);
+               NV_WR32(par->PRAMIN, 0x0002 * 4, 0x80000011);
+               NV_WR32(par->PRAMIN, 0x0003 * 4, 0x00101204);
+               NV_WR32(par->PRAMIN, 0x0004 * 4, 0x80000012);
+               NV_WR32(par->PRAMIN, 0x0005 * 4, 0x00101206);
+               NV_WR32(par->PRAMIN, 0x0006 * 4, 0x80000013);
+               NV_WR32(par->PRAMIN, 0x0007 * 4, 0x00101208);
+               NV_WR32(par->PRAMIN, 0x0008 * 4, 0x80000014);
+               NV_WR32(par->PRAMIN, 0x0009 * 4, 0x0010120A);
+               NV_WR32(par->PRAMIN, 0x000A * 4, 0x80000015);
+               NV_WR32(par->PRAMIN, 0x000B * 4, 0x0010120C);
+               NV_WR32(par->PRAMIN, 0x000C * 4, 0x80000016);
+               NV_WR32(par->PRAMIN, 0x000D * 4, 0x0010120E);
+               NV_WR32(par->PRAMIN, 0x000E * 4, 0x80000017);
+               NV_WR32(par->PRAMIN, 0x000F * 4, 0x00101210);
+               NV_WR32(par->PRAMIN, 0x0800 * 4, 0x00003000);
+               NV_WR32(par->PRAMIN, 0x0801 * 4, par->FbMapSize - 1);
+               NV_WR32(par->PRAMIN, 0x0802 * 4, 0x00000002);
+               NV_WR32(par->PRAMIN, 0x0808 * 4, 0x02080062);
+               NV_WR32(par->PRAMIN, 0x0809 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x080A * 4, 0x00001200);
+               NV_WR32(par->PRAMIN, 0x080B * 4, 0x00001200);
+               NV_WR32(par->PRAMIN, 0x080C * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x080D * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0810 * 4, 0x02080043);
+               NV_WR32(par->PRAMIN, 0x0811 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0812 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0813 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0814 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0815 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0818 * 4, 0x02080044);
+               NV_WR32(par->PRAMIN, 0x0819 * 4, 0x02000000);
+               NV_WR32(par->PRAMIN, 0x081A * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x081B * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x081C * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x081D * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0820 * 4, 0x02080019);
+               NV_WR32(par->PRAMIN, 0x0821 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0822 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0823 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0824 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0825 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0828 * 4, 0x020A005C);
+               NV_WR32(par->PRAMIN, 0x0829 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x082A * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x082B * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x082C * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x082D * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0830 * 4, 0x0208009F);
+               NV_WR32(par->PRAMIN, 0x0831 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0832 * 4, 0x00001200);
+               NV_WR32(par->PRAMIN, 0x0833 * 4, 0x00001200);
+               NV_WR32(par->PRAMIN, 0x0834 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0835 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0838 * 4, 0x0208004A);
+               NV_WR32(par->PRAMIN, 0x0839 * 4, 0x02000000);
+               NV_WR32(par->PRAMIN, 0x083A * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x083B * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x083C * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x083D * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0840 * 4, 0x02080077);
+               NV_WR32(par->PRAMIN, 0x0841 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0842 * 4, 0x00001200);
+               NV_WR32(par->PRAMIN, 0x0843 * 4, 0x00001200);
+               NV_WR32(par->PRAMIN, 0x0844 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0845 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x084C * 4, 0x00003002);
+               NV_WR32(par->PRAMIN, 0x084D * 4, 0x00007FFF);
+               NV_WR32(par->PRAMIN, 0x084E * 4,
+                       par->FbUsableSize | 0x00000002);
+
+#ifdef __BIG_ENDIAN
+               NV_WR32(par->PRAMIN, 0x080A * 4,
+                       NV_RD32(par->PRAMIN, 0x080A * 4) | 0x01000000);
+               NV_WR32(par->PRAMIN, 0x0812 * 4,
+                       NV_RD32(par->PRAMIN, 0x0812 * 4) | 0x01000000);
+               NV_WR32(par->PRAMIN, 0x081A * 4,
+                       NV_RD32(par->PRAMIN, 0x081A * 4) | 0x01000000);
+               NV_WR32(par->PRAMIN, 0x0822 * 4,
+                       NV_RD32(par->PRAMIN, 0x0822 * 4) | 0x01000000);
+               NV_WR32(par->PRAMIN, 0x082A * 4,
+                       NV_RD32(par->PRAMIN, 0x082A * 4) | 0x01000000);
+               NV_WR32(par->PRAMIN, 0x0832 * 4,
+                       NV_RD32(par->PRAMIN, 0x0832 * 4) | 0x01000000);
+               NV_WR32(par->PRAMIN, 0x083A * 4,
+                       NV_RD32(par->PRAMIN, 0x083A * 4) | 0x01000000);
+               NV_WR32(par->PRAMIN, 0x0842 * 4,
+                       NV_RD32(par->PRAMIN, 0x0842 * 4) | 0x01000000);
+               NV_WR32(par->PRAMIN, 0x0819 * 4, 0x01000000);
+               NV_WR32(par->PRAMIN, 0x0839 * 4, 0x01000000);
+#endif
+       } else {
+               NV_WR32(par->PRAMIN, 0x0000 * 4, 0x80000010);
+               NV_WR32(par->PRAMIN, 0x0001 * 4, 0x80011201);
+               NV_WR32(par->PRAMIN, 0x0002 * 4, 0x80000011);
+               NV_WR32(par->PRAMIN, 0x0003 * 4, 0x80011202);
+               NV_WR32(par->PRAMIN, 0x0004 * 4, 0x80000012);
+               NV_WR32(par->PRAMIN, 0x0005 * 4, 0x80011203);
+               NV_WR32(par->PRAMIN, 0x0006 * 4, 0x80000013);
+               NV_WR32(par->PRAMIN, 0x0007 * 4, 0x80011204);
+               NV_WR32(par->PRAMIN, 0x0008 * 4, 0x80000014);
+               NV_WR32(par->PRAMIN, 0x0009 * 4, 0x80011205);
+               NV_WR32(par->PRAMIN, 0x000A * 4, 0x80000015);
+               NV_WR32(par->PRAMIN, 0x000B * 4, 0x80011206);
+               NV_WR32(par->PRAMIN, 0x000C * 4, 0x80000016);
+               NV_WR32(par->PRAMIN, 0x000D * 4, 0x80011207);
+               NV_WR32(par->PRAMIN, 0x000E * 4, 0x80000017);
+               NV_WR32(par->PRAMIN, 0x000F * 4, 0x80011208);
+               NV_WR32(par->PRAMIN, 0x0800 * 4, 0x00003000);
+               NV_WR32(par->PRAMIN, 0x0801 * 4, par->FbMapSize - 1);
+               NV_WR32(par->PRAMIN, 0x0802 * 4, 0x00000002);
+               NV_WR32(par->PRAMIN, 0x0803 * 4, 0x00000002);
+               if (par->Architecture >= NV_ARCH_10)
+                       NV_WR32(par->PRAMIN, 0x0804 * 4, 0x01008062);
+               else
+                       NV_WR32(par->PRAMIN, 0x0804 * 4, 0x01008042);
+               NV_WR32(par->PRAMIN, 0x0805 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0806 * 4, 0x12001200);
+               NV_WR32(par->PRAMIN, 0x0807 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0808 * 4, 0x01008043);
+               NV_WR32(par->PRAMIN, 0x0809 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x080A * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x080B * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x080C * 4, 0x01008044);
+               NV_WR32(par->PRAMIN, 0x080D * 4, 0x00000002);
+               NV_WR32(par->PRAMIN, 0x080E * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x080F * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0810 * 4, 0x01008019);
+               NV_WR32(par->PRAMIN, 0x0811 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0812 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0813 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0814 * 4, 0x0100A05C);
+               NV_WR32(par->PRAMIN, 0x0815 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0816 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0817 * 4, 0x00000000);
+               if (par->WaitVSyncPossible)
+                       NV_WR32(par->PRAMIN, 0x0818 * 4, 0x0100809F);
+               else
+                       NV_WR32(par->PRAMIN, 0x0818 * 4, 0x0100805F);
+               NV_WR32(par->PRAMIN, 0x0819 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x081A * 4, 0x12001200);
+               NV_WR32(par->PRAMIN, 0x081B * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x081C * 4, 0x0100804A);
+               NV_WR32(par->PRAMIN, 0x081D * 4, 0x00000002);
+               NV_WR32(par->PRAMIN, 0x081E * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x081F * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0820 * 4, 0x01018077);
+               NV_WR32(par->PRAMIN, 0x0821 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0822 * 4, 0x12001200);
+               NV_WR32(par->PRAMIN, 0x0823 * 4, 0x00000000);
+               NV_WR32(par->PRAMIN, 0x0824 * 4, 0x00003002);
+               NV_WR32(par->PRAMIN, 0x0825 * 4, 0x00007FFF);
+               NV_WR32(par->PRAMIN, 0x0826 * 4,
+                       par->FbUsableSize | 0x00000002);
+               NV_WR32(par->PRAMIN, 0x0827 * 4, 0x00000002);
+#ifdef __BIG_ENDIAN
+               NV_WR32(par->PRAMIN, 0x0804 * 4,
+                       NV_RD32(par->PRAMIN, 0x0804 * 4) | 0x00080000);
+               NV_WR32(par->PRAMIN, 0x0808 * 4,
+                       NV_RD32(par->PRAMIN, 0x0808 * 4) | 0x00080000);
+               NV_WR32(par->PRAMIN, 0x080C * 4,
+                       NV_RD32(par->PRAMIN, 0x080C * 4) | 0x00080000);
+               NV_WR32(par->PRAMIN, 0x0810 * 4,
+                       NV_RD32(par->PRAMIN, 0x0810 * 4) | 0x00080000);
+               NV_WR32(par->PRAMIN, 0x0814 * 4,
+                       NV_RD32(par->PRAMIN, 0x0814 * 4) | 0x00080000);
+               NV_WR32(par->PRAMIN, 0x0818 * 4,
+                       NV_RD32(par->PRAMIN, 0x0818 * 4) | 0x00080000);
+               NV_WR32(par->PRAMIN, 0x081C * 4,
+                       NV_RD32(par->PRAMIN, 0x081C * 4) | 0x00080000);
+               NV_WR32(par->PRAMIN, 0x0820 * 4,
+                       NV_RD32(par->PRAMIN, 0x0820 * 4) | 0x00080000);
+               NV_WR32(par->PRAMIN, 0x080D * 4, 0x00000001);
+               NV_WR32(par->PRAMIN, 0x081D * 4, 0x00000001);
+#endif
+       }
+       if (par->Architecture < NV_ARCH_10) {
+               if ((par->Chipset & 0x0fff) == 0x0020) {
+                       NV_WR32(par->PRAMIN, 0x0824 * 4,
+                               NV_RD32(par->PRAMIN, 0x0824 * 4) | 0x00020000);
+                       NV_WR32(par->PRAMIN, 0x0826 * 4,
+                               NV_RD32(par->PRAMIN,
+                                       0x0826 * 4) + par->FbAddress);
+               }
+               NV_WR32(par->PGRAPH, 0x0080, 0x000001FF);
+               NV_WR32(par->PGRAPH, 0x0080, 0x1230C000);
+               NV_WR32(par->PGRAPH, 0x0084, 0x72111101);
+               NV_WR32(par->PGRAPH, 0x0088, 0x11D5F071);
+               NV_WR32(par->PGRAPH, 0x008C, 0x0004FF31);
+               NV_WR32(par->PGRAPH, 0x008C, 0x4004FF31);
+               NV_WR32(par->PGRAPH, 0x0140, 0x00000000);
+               NV_WR32(par->PGRAPH, 0x0100, 0xFFFFFFFF);
+               NV_WR32(par->PGRAPH, 0x0170, 0x10010100);
+               NV_WR32(par->PGRAPH, 0x0710, 0xFFFFFFFF);
+               NV_WR32(par->PGRAPH, 0x0720, 0x00000001);
+               NV_WR32(par->PGRAPH, 0x0810, 0x00000000);
+               NV_WR32(par->PGRAPH, 0x0608, 0xFFFFFFFF);
+       } else {
+               NV_WR32(par->PGRAPH, 0x0080, 0xFFFFFFFF);
+               NV_WR32(par->PGRAPH, 0x0080, 0x00000000);
+
+               NV_WR32(par->PGRAPH, 0x0140, 0x00000000);
+               NV_WR32(par->PGRAPH, 0x0100, 0xFFFFFFFF);
+               NV_WR32(par->PGRAPH, 0x0144, 0x10010100);
+               NV_WR32(par->PGRAPH, 0x0714, 0xFFFFFFFF);
+               NV_WR32(par->PGRAPH, 0x0720, 0x00000001);
+               NV_WR32(par->PGRAPH, 0x0710,
+                       NV_RD32(par->PGRAPH, 0x0710) & 0x0007ff00);
+               NV_WR32(par->PGRAPH, 0x0710,
+                       NV_RD32(par->PGRAPH, 0x0710) | 0x00020100);
+
+               if (par->Architecture == NV_ARCH_10) {
+                       NV_WR32(par->PGRAPH, 0x0084, 0x00118700);
+                       NV_WR32(par->PGRAPH, 0x0088, 0x24E00810);
+                       NV_WR32(par->PGRAPH, 0x008C, 0x55DE0030);
+
+                       for (i = 0; i < 32; i++)
+                               NV_WR32(&par->PGRAPH[(0x0B00 / 4) + i], 0,
+                                       NV_RD32(&par->PFB[(0x0240 / 4) + i],
+                                               0));
+
+                       NV_WR32(par->PGRAPH, 0x640, 0);
+                       NV_WR32(par->PGRAPH, 0x644, 0);
+                       NV_WR32(par->PGRAPH, 0x684, par->FbMapSize - 1);
+                       NV_WR32(par->PGRAPH, 0x688, par->FbMapSize - 1);
+
+                       NV_WR32(par->PGRAPH, 0x0810, 0x00000000);
+                       NV_WR32(par->PGRAPH, 0x0608, 0xFFFFFFFF);
+               } else {
+                       if (par->Architecture >= NV_ARCH_40) {
+                               NV_WR32(par->PGRAPH, 0x0084, 0x401287c0);
+                               NV_WR32(par->PGRAPH, 0x008C, 0x60de8051);
+                               NV_WR32(par->PGRAPH, 0x0090, 0x00008000);
+                               NV_WR32(par->PGRAPH, 0x0610, 0x00be3c5f);
+
+                               if ((par->Chipset & 0xfff0) == 0x0040) {
+                                       NV_WR32(par->PGRAPH, 0x09b0,
+                                               0x83280fff);
+                                       NV_WR32(par->PGRAPH, 0x09b4,
+                                               0x000000a0);
+                               } else {
+                                       NV_WR32(par->PGRAPH, 0x0820,
+                                               0x83280eff);
+                                       NV_WR32(par->PGRAPH, 0x0824,
+                                               0x000000a0);
+                               }
+
+                               switch (par->Chipset & 0xfff0) {
+                               case 0x0040:
+                               case 0x0210:
+                                       NV_WR32(par->PGRAPH, 0x09b8,
+                                               0x0078e366);
+                                       NV_WR32(par->PGRAPH, 0x09bc,
+                                               0x0000014c);
+                                       NV_WR32(par->PFB, 0x033C,
+                                               NV_RD32(par->PFB, 0x33C) &
+                                               0xffff7fff);
+                                       break;
+                               case 0x00C0:
+                                       NV_WR32(par->PGRAPH, 0x0828,
+                                               0x007596ff);
+                                       NV_WR32(par->PGRAPH, 0x082C,
+                                               0x00000108);
+                                       break;
+                               case 0x0160:
+                               case 0x01D0:
+                                       NV_WR32(par->PMC, 0x1700,
+                                               NV_RD32(par->PFB, 0x020C));
+                                       NV_WR32(par->PMC, 0x1704, 0);
+                                       NV_WR32(par->PMC, 0x1708, 0);
+                                       NV_WR32(par->PMC, 0x170C,
+                                               NV_RD32(par->PFB, 0x020C));
+                                       NV_WR32(par->PGRAPH, 0x0860, 0);
+                                       NV_WR32(par->PGRAPH, 0x0864, 0);
+                                       NV_WR32(par->PRAMDAC, 0x0608,
+                                               NV_RD32(par->PRAMDAC,
+                                                       0x0608) | 0x00100000);
+                                       break;
+                               case 0x0140:
+                                       NV_WR32(par->PGRAPH, 0x0828,
+                                               0x0072cb77);
+                                       NV_WR32(par->PGRAPH, 0x082C,
+                                               0x00000108);
+                                       break;
+                               case 0x0220:
+                               case 0x0230:
+                                       NV_WR32(par->PGRAPH, 0x0860, 0);
+                                       NV_WR32(par->PGRAPH, 0x0864, 0);
+                                       NV_WR32(par->PRAMDAC, 0x0608,
+                                               NV_RD32(par->PRAMDAC, 0x0608) |
+                                               0x00100000);
+                                       break;
+                               case 0x0090:
+                                       NV_WR32(par->PRAMDAC, 0x0608,
+                                               NV_RD32(par->PRAMDAC, 0x0608) |
+                                               0x00100000);
+                                       NV_WR32(par->PGRAPH, 0x0828,
+                                               0x07830610);
+                                       NV_WR32(par->PGRAPH, 0x082C,
+                                               0x0000016A);
+                                       break;
+                               default:
+                                       break;
+                               };
+
+                               NV_WR32(par->PGRAPH, 0x0b38, 0x2ffff800);
+                               NV_WR32(par->PGRAPH, 0x0b3c, 0x00006000);
+                               NV_WR32(par->PGRAPH, 0x032C, 0x01000000);
+                               NV_WR32(par->PGRAPH, 0x0220, 0x00001200);
+                       } else if (par->Architecture == NV_ARCH_30) {
+                               NV_WR32(par->PGRAPH, 0x0084, 0x40108700);
+                               NV_WR32(par->PGRAPH, 0x0890, 0x00140000);
+                               NV_WR32(par->PGRAPH, 0x008C, 0xf00e0431);
+                               NV_WR32(par->PGRAPH, 0x0090, 0x00008000);
+                               NV_WR32(par->PGRAPH, 0x0610, 0xf04b1f36);
+                               NV_WR32(par->PGRAPH, 0x0B80, 0x1002d888);
+                               NV_WR32(par->PGRAPH, 0x0B88, 0x62ff007f);
+                       } else {
+                               NV_WR32(par->PGRAPH, 0x0084, 0x00118700);
+                               NV_WR32(par->PGRAPH, 0x008C, 0xF20E0431);
+                               NV_WR32(par->PGRAPH, 0x0090, 0x00000000);
+                               NV_WR32(par->PGRAPH, 0x009C, 0x00000040);
+
+                               if ((par->Chipset & 0x0ff0) >= 0x0250) {
+                                       NV_WR32(par->PGRAPH, 0x0890,
+                                               0x00080000);
+                                       NV_WR32(par->PGRAPH, 0x0610,
+                                               0x304B1FB6);
+                                       NV_WR32(par->PGRAPH, 0x0B80,
+                                               0x18B82880);
+                                       NV_WR32(par->PGRAPH, 0x0B84,
+                                               0x44000000);
+                                       NV_WR32(par->PGRAPH, 0x0098,
+                                               0x40000080);
+                                       NV_WR32(par->PGRAPH, 0x0B88,
+                                               0x000000ff);
+                               } else {
+                                       NV_WR32(par->PGRAPH, 0x0880,
+                                               0x00080000);
+                                       NV_WR32(par->PGRAPH, 0x0094,
+                                               0x00000005);
+                                       NV_WR32(par->PGRAPH, 0x0B80,
+                                               0x45CAA208);
+                                       NV_WR32(par->PGRAPH, 0x0B84,
+                                               0x24000000);
+                                       NV_WR32(par->PGRAPH, 0x0098,
+                                               0x00000040);
+                                       NV_WR32(par->PGRAPH, 0x0750,
+                                               0x00E00038);
+                                       NV_WR32(par->PGRAPH, 0x0754,
+                                               0x00000030);
+                                       NV_WR32(par->PGRAPH, 0x0750,
+                                               0x00E10038);
+                                       NV_WR32(par->PGRAPH, 0x0754,
+                                               0x00000030);
+                               }
+                       }
+
+                       if ((par->Chipset & 0xfff0) == 0x0090) {
+                               for (i = 0; i < 60; i++)
+                                       NV_WR32(par->PGRAPH, 0x0D00 + i,
+                                               NV_RD32(par->PFB, 0x0600 + i));
+                       } else {
+                               for (i = 0; i < 32; i++)
+                                       NV_WR32(par->PGRAPH, 0x0900 + i,
+                                               NV_RD32(par->PFB, 0x0240 + i));
+                       }
+
+                       if (par->Architecture >= NV_ARCH_40) {
+                               if ((par->Chipset & 0xfff0) == 0x0040) {
+                                       NV_WR32(par->PGRAPH, 0x09A4,
+                                               NV_RD32(par->PFB, 0x0200));
+                                       NV_WR32(par->PGRAPH, 0x09A8,
+                                               NV_RD32(par->PFB, 0x0204));
+                                       NV_WR32(par->PGRAPH, 0x69A4,
+                                               NV_RD32(par->PFB, 0x0200));
+                                       NV_WR32(par->PGRAPH, 0x69A8,
+                                               NV_RD32(par->PFB, 0x0204));
+
+                                       NV_WR32(par->PGRAPH, 0x0820, 0);
+                                       NV_WR32(par->PGRAPH, 0x0824, 0);
+                                       NV_WR32(par->PGRAPH, 0x0864,
+                                               par->FbMapSize - 1);
+                                       NV_WR32(par->PGRAPH, 0x0868,
+                                               par->FbMapSize - 1);
+                               } else {
+                                       if((par->Chipset & 0xfff0) == 0x0090) {
+                                               NV_WR32(par->PGRAPH, 0x0DF0,
+                                                       NV_RD32(par->PFB, 0x0200));
+                                               NV_WR32(par->PGRAPH, 0x0DF4,
+                                                       NV_RD32(par->PFB, 0x0204));
+                                       } else {
+                                               NV_WR32(par->PGRAPH, 0x09F0,
+                                                       NV_RD32(par->PFB, 0x0200));
+                                               NV_WR32(par->PGRAPH, 0x09F4,
+                                                       NV_RD32(par->PFB, 0x0204));
+                                       }
+                                       NV_WR32(par->PGRAPH, 0x69F0,
+                                               NV_RD32(par->PFB, 0x0200));
+                                       NV_WR32(par->PGRAPH, 0x69F4,
+                                               NV_RD32(par->PFB, 0x0204));
+
+                                       NV_WR32(par->PGRAPH, 0x0840, 0);
+                                       NV_WR32(par->PGRAPH, 0x0844, 0);
+                                       NV_WR32(par->PGRAPH, 0x08a0,
+                                               par->FbMapSize - 1);
+                                       NV_WR32(par->PGRAPH, 0x08a4,
+                                               par->FbMapSize - 1);
+                               }
+                       } else {
+                               NV_WR32(par->PGRAPH, 0x09A4,
+                                       NV_RD32(par->PFB, 0x0200));
+                               NV_WR32(par->PGRAPH, 0x09A8,
+                                       NV_RD32(par->PFB, 0x0204));
+                               NV_WR32(par->PGRAPH, 0x0750, 0x00EA0000);
+                               NV_WR32(par->PGRAPH, 0x0754,
+                                       NV_RD32(par->PFB, 0x0200));
+                               NV_WR32(par->PGRAPH, 0x0750, 0x00EA0004);
+                               NV_WR32(par->PGRAPH, 0x0754,
+                                       NV_RD32(par->PFB, 0x0204));
+
+                               NV_WR32(par->PGRAPH, 0x0820, 0);
+                               NV_WR32(par->PGRAPH, 0x0824, 0);
+                               NV_WR32(par->PGRAPH, 0x0864,
+                                       par->FbMapSize - 1);
+                               NV_WR32(par->PGRAPH, 0x0868,
+                                       par->FbMapSize - 1);
+                       }
+                       NV_WR32(par->PGRAPH, 0x0B20, 0x00000000);
+                       NV_WR32(par->PGRAPH, 0x0B04, 0xFFFFFFFF);
+               }
+       }
+       NV_WR32(par->PGRAPH, 0x053C, 0);
+       NV_WR32(par->PGRAPH, 0x0540, 0);
+       NV_WR32(par->PGRAPH, 0x0544, 0x00007FFF);
+       NV_WR32(par->PGRAPH, 0x0548, 0x00007FFF);
+
+       NV_WR32(par->PFIFO, 0x0140 * 4, 0x00000000);
+       NV_WR32(par->PFIFO, 0x0141 * 4, 0x00000001);
+       NV_WR32(par->PFIFO, 0x0480 * 4, 0x00000000);
+       NV_WR32(par->PFIFO, 0x0494 * 4, 0x00000000);
+       if (par->Architecture >= NV_ARCH_40)
+               NV_WR32(par->PFIFO, 0x0481 * 4, 0x00010000);
+       else
+               NV_WR32(par->PFIFO, 0x0481 * 4, 0x00000100);
+       NV_WR32(par->PFIFO, 0x0490 * 4, 0x00000000);
+       NV_WR32(par->PFIFO, 0x0491 * 4, 0x00000000);
+       if (par->Architecture >= NV_ARCH_40)
+               NV_WR32(par->PFIFO, 0x048B * 4, 0x00001213);
+       else
+               NV_WR32(par->PFIFO, 0x048B * 4, 0x00001209);
+       NV_WR32(par->PFIFO, 0x0400 * 4, 0x00000000);
+       NV_WR32(par->PFIFO, 0x0414 * 4, 0x00000000);
+       NV_WR32(par->PFIFO, 0x0084 * 4, 0x03000100);
+       NV_WR32(par->PFIFO, 0x0085 * 4, 0x00000110);
+       NV_WR32(par->PFIFO, 0x0086 * 4, 0x00000112);
+       NV_WR32(par->PFIFO, 0x0143 * 4, 0x0000FFFF);
+       NV_WR32(par->PFIFO, 0x0496 * 4, 0x0000FFFF);
+       NV_WR32(par->PFIFO, 0x0050 * 4, 0x00000000);
+       NV_WR32(par->PFIFO, 0x0040 * 4, 0xFFFFFFFF);
+       NV_WR32(par->PFIFO, 0x0415 * 4, 0x00000001);
+       NV_WR32(par->PFIFO, 0x048C * 4, 0x00000000);
+       NV_WR32(par->PFIFO, 0x04A0 * 4, 0x00000000);
+#ifdef __BIG_ENDIAN
+       NV_WR32(par->PFIFO, 0x0489 * 4, 0x800F0078);
+#else
+       NV_WR32(par->PFIFO, 0x0489 * 4, 0x000F0078);
+#endif
+       NV_WR32(par->PFIFO, 0x0488 * 4, 0x00000001);
+       NV_WR32(par->PFIFO, 0x0480 * 4, 0x00000001);
+       NV_WR32(par->PFIFO, 0x0494 * 4, 0x00000001);
+       NV_WR32(par->PFIFO, 0x0495 * 4, 0x00000001);
+       NV_WR32(par->PFIFO, 0x0140 * 4, 0x00000001);
+       if (par->Architecture >= NV_ARCH_10) {
+               if (par->twoHeads) {
+                       NV_WR32(par->PCRTC0, 0x0860, state->head);
+                       NV_WR32(par->PCRTC0, 0x2860, state->head2);
+               }
+               NV_WR32(par->PRAMDAC, 0x0404, NV_RD32(par->PRAMDAC, 0x0404) |
+                       (1 << 25));
+
+               NV_WR32(par->PMC, 0x8704, 1);
+               NV_WR32(par->PMC, 0x8140, 0);
+               NV_WR32(par->PMC, 0x8920, 0);
+               NV_WR32(par->PMC, 0x8924, 0);
+               NV_WR32(par->PMC, 0x8908, par->FbMapSize - 1);
+               NV_WR32(par->PMC, 0x890C, par->FbMapSize - 1);
+               NV_WR32(par->PMC, 0x1588, 0);
+
+               NV_WR32(par->PCRTC, 0x0810, state->cursorConfig);
+               NV_WR32(par->PCRTC, 0x0830, state->displayV - 3);
+               NV_WR32(par->PCRTC, 0x0834, state->displayV - 1);
+
+               if (par->FlatPanel) {
+                       if ((par->Chipset & 0x0ff0) == 0x0110) {
+                               NV_WR32(par->PRAMDAC, 0x0528, state->dither);
+                       } else if (par->twoHeads) {
+                               NV_WR32(par->PRAMDAC, 0x083C, state->dither);
+                       }
+
+                       VGA_WR08(par->PCIO, 0x03D4, 0x53);
+                       VGA_WR08(par->PCIO, 0x03D5, state->timingH);
+                       VGA_WR08(par->PCIO, 0x03D4, 0x54);
+                       VGA_WR08(par->PCIO, 0x03D5, state->timingV);
+                       VGA_WR08(par->PCIO, 0x03D4, 0x21);
+                       VGA_WR08(par->PCIO, 0x03D5, 0xfa);
+               }
+
+               VGA_WR08(par->PCIO, 0x03D4, 0x41);
+               VGA_WR08(par->PCIO, 0x03D5, state->extra);
+       }
+
+       VGA_WR08(par->PCIO, 0x03D4, 0x19);
+       VGA_WR08(par->PCIO, 0x03D5, state->repaint0);
+       VGA_WR08(par->PCIO, 0x03D4, 0x1A);
+       VGA_WR08(par->PCIO, 0x03D5, state->repaint1);
+       VGA_WR08(par->PCIO, 0x03D4, 0x25);
+       VGA_WR08(par->PCIO, 0x03D5, state->screen);
+       VGA_WR08(par->PCIO, 0x03D4, 0x28);
+       VGA_WR08(par->PCIO, 0x03D5, state->pixel);
+       VGA_WR08(par->PCIO, 0x03D4, 0x2D);
+       VGA_WR08(par->PCIO, 0x03D5, state->horiz);
+       VGA_WR08(par->PCIO, 0x03D4, 0x1C);
+       VGA_WR08(par->PCIO, 0x03D5, state->fifo);
+       VGA_WR08(par->PCIO, 0x03D4, 0x1B);
+       VGA_WR08(par->PCIO, 0x03D5, state->arbitration0);
+       VGA_WR08(par->PCIO, 0x03D4, 0x20);
+       VGA_WR08(par->PCIO, 0x03D5, state->arbitration1);
+
+       if(par->Architecture >= NV_ARCH_30) {
+               VGA_WR08(par->PCIO, 0x03D4, 0x47);
+               VGA_WR08(par->PCIO, 0x03D5, state->arbitration1 >> 8);
+       }
+
+       VGA_WR08(par->PCIO, 0x03D4, 0x30);
+       VGA_WR08(par->PCIO, 0x03D5, state->cursor0);
+       VGA_WR08(par->PCIO, 0x03D4, 0x31);
+       VGA_WR08(par->PCIO, 0x03D5, state->cursor1);
+       VGA_WR08(par->PCIO, 0x03D4, 0x2F);
+       VGA_WR08(par->PCIO, 0x03D5, state->cursor2);
+       VGA_WR08(par->PCIO, 0x03D4, 0x39);
+       VGA_WR08(par->PCIO, 0x03D5, state->interlace);
+
+       if (!par->FlatPanel) {
+               NV_WR32(par->PRAMDAC0, 0x050C, state->pllsel);
+               NV_WR32(par->PRAMDAC0, 0x0508, state->vpll);
+               if (par->twoHeads)
+                       NV_WR32(par->PRAMDAC0, 0x0520, state->vpll2);
+               if (par->twoStagePLL) {
+                       NV_WR32(par->PRAMDAC0, 0x0578, state->vpllB);
+                       NV_WR32(par->PRAMDAC0, 0x057C, state->vpll2B);
+               }
+       } else {
+               NV_WR32(par->PRAMDAC, 0x0848, state->scale);
+               NV_WR32(par->PRAMDAC, 0x0828, state->crtcSync +
+                       par->PanelTweak);
+       }
+
+       NV_WR32(par->PRAMDAC, 0x0600, state->general);
+
+       NV_WR32(par->PCRTC, 0x0140, 0);
+       NV_WR32(par->PCRTC, 0x0100, 1);
+
+       par->CurrentState = state;
+}
+
+void NVUnloadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state) {
+       VGA_WR08(par->PCIO, 0x03D4, 0x19);
+       state->repaint0 = VGA_RD08(par->PCIO, 0x03D5);
+       VGA_WR08(par->PCIO, 0x03D4, 0x1A);
+       state->repaint1 = VGA_RD08(par->PCIO, 0x03D5);
+       VGA_WR08(par->PCIO, 0x03D4, 0x25);
+       state->screen = VGA_RD08(par->PCIO, 0x03D5);
+       VGA_WR08(par->PCIO, 0x03D4, 0x28);
+       state->pixel = VGA_RD08(par->PCIO, 0x03D5);
+       VGA_WR08(par->PCIO, 0x03D4, 0x2D);
+       state->horiz = VGA_RD08(par->PCIO, 0x03D5);
+       VGA_WR08(par->PCIO, 0x03D4, 0x1C);
+       state->fifo         = VGA_RD08(par->PCIO, 0x03D5);
+       VGA_WR08(par->PCIO, 0x03D4, 0x1B);
+       state->arbitration0 = VGA_RD08(par->PCIO, 0x03D5);
+       VGA_WR08(par->PCIO, 0x03D4, 0x20);
+       state->arbitration1 = VGA_RD08(par->PCIO, 0x03D5);
+
+       if(par->Architecture >= NV_ARCH_30) {
+               VGA_WR08(par->PCIO, 0x03D4, 0x47);
+               state->arbitration1 |= (VGA_RD08(par->PCIO, 0x03D5) & 1) << 8;
+       }
+
+       VGA_WR08(par->PCIO, 0x03D4, 0x30);
+       state->cursor0 = VGA_RD08(par->PCIO, 0x03D5);
+       VGA_WR08(par->PCIO, 0x03D4, 0x31);
+       state->cursor1 = VGA_RD08(par->PCIO, 0x03D5);
+       VGA_WR08(par->PCIO, 0x03D4, 0x2F);
+       state->cursor2 = VGA_RD08(par->PCIO, 0x03D5);
+       VGA_WR08(par->PCIO, 0x03D4, 0x39);
+       state->interlace = VGA_RD08(par->PCIO, 0x03D5);
+       state->vpll = NV_RD32(par->PRAMDAC0, 0x0508);
+       if (par->twoHeads)
+               state->vpll2 = NV_RD32(par->PRAMDAC0, 0x0520);
+       if (par->twoStagePLL) {
+               state->vpllB = NV_RD32(par->PRAMDAC0, 0x0578);
+               state->vpll2B = NV_RD32(par->PRAMDAC0, 0x057C);
+       }
+       state->pllsel = NV_RD32(par->PRAMDAC0, 0x050C);
+       state->general = NV_RD32(par->PRAMDAC, 0x0600);
+       state->scale = NV_RD32(par->PRAMDAC, 0x0848);
+       state->config = NV_RD32(par->PFB, 0x0200);
+
+       if (par->Architecture >= NV_ARCH_10) {
+               if (par->twoHeads) {
+                       state->head = NV_RD32(par->PCRTC0, 0x0860);
+                       state->head2 = NV_RD32(par->PCRTC0, 0x2860);
+                       VGA_WR08(par->PCIO, 0x03D4, 0x44);
+                       state->crtcOwner = VGA_RD08(par->PCIO, 0x03D5);
+               }
+               VGA_WR08(par->PCIO, 0x03D4, 0x41);
+               state->extra = VGA_RD08(par->PCIO, 0x03D5);
+               state->cursorConfig = NV_RD32(par->PCRTC, 0x0810);
+
+               if ((par->Chipset & 0x0ff0) == 0x0110) {
+                       state->dither = NV_RD32(par->PRAMDAC, 0x0528);
+               } else if (par->twoHeads) {
+                       state->dither = NV_RD32(par->PRAMDAC, 0x083C);
+               }
+
+               if (par->FlatPanel) {
+                       VGA_WR08(par->PCIO, 0x03D4, 0x53);
+                       state->timingH = VGA_RD08(par->PCIO, 0x03D5);
+                       VGA_WR08(par->PCIO, 0x03D4, 0x54);
+                       state->timingV = VGA_RD08(par->PCIO, 0x03D5);
+               }
+       }
+}
+
+void NVSetStartAddress(struct nvidia_par *par, u32 start)
+{
+       NV_WR32(par->PCRTC, 0x800, start);
+}
diff --git a/drivers/video/nvidia/nv_i2c.c b/drivers/video/nvidia/nv_i2c.c
new file mode 100644 (file)
index 0000000..3757c14
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * linux/drivers/video/nvidia/nvidia-i2c.c - nVidia i2c
+ *
+ * Copyright 2004 Antonino A. Daplas <adaplas @pol.net>
+ *
+ * Based 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 "nv_type.h"
+#include "nv_local.h"
+#include "nv_proto.h"
+
+#include "../edid.h"
+
+static void nvidia_gpio_setscl(void *data, int state)
+{
+       struct nvidia_i2c_chan *chan = data;
+       struct nvidia_par *par = chan->par;
+       u32 val;
+
+       VGA_WR08(par->PCIO, 0x3d4, chan->ddc_base + 1);
+       val = VGA_RD08(par->PCIO, 0x3d5) & 0xf0;
+
+       if (state)
+               val |= 0x20;
+       else
+               val &= ~0x20;
+
+       VGA_WR08(par->PCIO, 0x3d4, chan->ddc_base + 1);
+       VGA_WR08(par->PCIO, 0x3d5, val | 0x1);
+}
+
+static void nvidia_gpio_setsda(void *data, int state)
+{
+       struct nvidia_i2c_chan *chan = (struct nvidia_i2c_chan *)data;
+       struct nvidia_par *par = chan->par;
+       u32 val;
+
+       VGA_WR08(par->PCIO, 0x3d4, chan->ddc_base + 1);
+       val = VGA_RD08(par->PCIO, 0x3d5) & 0xf0;
+
+       if (state)
+               val |= 0x10;
+       else
+               val &= ~0x10;
+
+       VGA_WR08(par->PCIO, 0x3d4, chan->ddc_base + 1);
+       VGA_WR08(par->PCIO, 0x3d5, val | 0x1);
+}
+
+static int nvidia_gpio_getscl(void *data)
+{
+       struct nvidia_i2c_chan *chan = (struct nvidia_i2c_chan *)data;
+       struct nvidia_par *par = chan->par;
+       u32 val = 0;
+
+       VGA_WR08(par->PCIO, 0x3d4, chan->ddc_base);
+       if (VGA_RD08(par->PCIO, 0x3d5) & 0x04)
+               val = 1;
+
+       val = VGA_RD08(par->PCIO, 0x3d5);
+
+       return val;
+}
+
+static int nvidia_gpio_getsda(void *data)
+{
+       struct nvidia_i2c_chan *chan = (struct nvidia_i2c_chan *)data;
+       struct nvidia_par *par = chan->par;
+       u32 val = 0;
+
+       VGA_WR08(par->PCIO, 0x3d4, chan->ddc_base);
+       if (VGA_RD08(par->PCIO, 0x3d5) & 0x08)
+               val = 1;
+
+       return val;
+}
+
+#define I2C_ALGO_NVIDIA   0x0e0000
+static int nvidia_setup_i2c_bus(struct nvidia_i2c_chan *chan, const char *name)
+{
+       int rc;
+
+       strcpy(chan->adapter.name, name);
+       chan->adapter.owner = THIS_MODULE;
+       chan->adapter.id = I2C_ALGO_NVIDIA;
+       chan->adapter.algo_data = &chan->algo;
+       chan->adapter.dev.parent = &chan->par->pci_dev->dev;
+       chan->algo.setsda = nvidia_gpio_setsda;
+       chan->algo.setscl = nvidia_gpio_setscl;
+       chan->algo.getsda = nvidia_gpio_getsda;
+       chan->algo.getscl = nvidia_gpio_getscl;
+       chan->algo.udelay = 40;
+       chan->algo.timeout = msecs_to_jiffies(2);
+       chan->algo.data = chan;
+
+       i2c_set_adapdata(&chan->adapter, chan);
+
+       /* Raise SCL and SDA */
+       nvidia_gpio_setsda(chan, 1);
+       nvidia_gpio_setscl(chan, 1);
+       udelay(20);
+
+       rc = i2c_bit_add_bus(&chan->adapter);
+       if (rc == 0)
+               dev_dbg(&chan->par->pci_dev->dev,
+                       "I2C bus %s registered.\n", name);
+       else {
+               dev_warn(&chan->par->pci_dev->dev,
+                        "Failed to register I2C bus %s.\n", name);
+               chan->par = NULL;
+       }
+
+       return rc;
+}
+
+void nvidia_create_i2c_busses(struct nvidia_par *par)
+{
+       par->bus = 3;
+
+       par->chan[0].par = par;
+       par->chan[1].par = par;
+       par->chan[2].par = par;
+
+       par->chan[0].ddc_base = 0x3e;
+       nvidia_setup_i2c_bus(&par->chan[0], "BUS1");
+
+       par->chan[1].ddc_base = 0x36;
+       nvidia_setup_i2c_bus(&par->chan[1], "BUS2");
+
+       par->chan[2].ddc_base = 0x50;
+       nvidia_setup_i2c_bus(&par->chan[2], "BUS3");
+}
+
+void nvidia_delete_i2c_busses(struct nvidia_par *par)
+{
+       if (par->chan[0].par)
+               i2c_bit_del_bus(&par->chan[0].adapter);
+       par->chan[0].par = NULL;
+
+       if (par->chan[1].par)
+               i2c_bit_del_bus(&par->chan[1].adapter);
+       par->chan[1].par = NULL;
+
+       if (par->chan[2].par)
+               i2c_bit_del_bus(&par->chan[2].adapter);
+       par->chan[2].par = NULL;
+
+}
+
+static u8 *nvidia_do_probe_i2c_edid(struct nvidia_i2c_chan *chan)
+{
+       u8 start = 0x0;
+       struct i2c_msg msgs[] = {
+               {
+                .addr = 0x50,
+                .len = 1,
+                .buf = &start,
+                }, {
+                    .addr = 0x50,
+                    .flags = I2C_M_RD,
+                    .len = EDID_LENGTH,
+                    },
+       };
+       u8 *buf;
+
+       if (!chan->par)
+               return NULL;
+
+       buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
+       if (!buf) {
+               dev_warn(&chan->par->pci_dev->dev, "Out of memory!\n");
+               return NULL;
+       }
+       msgs[1].buf = buf;
+
+       if (i2c_transfer(&chan->adapter, msgs, 2) == 2)
+               return buf;
+       dev_dbg(&chan->par->pci_dev->dev, "Unable to read EDID block.\n");
+       kfree(buf);
+       return NULL;
+}
+
+int nvidia_probe_i2c_connector(struct nvidia_par *par, int conn, u8 **out_edid)
+{
+       u8 *edid = NULL;
+       int i;
+
+       for (i = 0; i < 3; i++) {
+               /* Do the real work */
+               edid = nvidia_do_probe_i2c_edid(&par->chan[conn - 1]);
+               if (edid)
+                       break;
+       }
+       if (out_edid)
+               *out_edid = edid;
+       if (!edid)
+               return 1;
+
+       return 0;
+}
diff --git a/drivers/video/nvidia/nv_local.h b/drivers/video/nvidia/nv_local.h
new file mode 100644 (file)
index 0000000..9da3209
--- /dev/null
@@ -0,0 +1,107 @@
+/***************************************************************************\
+|*                                                                           *|
+|*       Copyright 1993-2003 NVIDIA, Corporation.  All rights reserved.      *|
+|*                                                                           *|
+|*     NOTICE TO USER:   The source code  is copyrighted under  U.S. and     *|
+|*     international laws.  Users and possessors of this source code are     *|
+|*     hereby granted a nonexclusive,  royalty-free copyright license to     *|
+|*     use this code in individual and commercial software.                  *|
+|*                                                                           *|
+|*     Any use of this source code must include,  in the user documenta-     *|
+|*     tion and  internal comments to the code,  notices to the end user     *|
+|*     as follows:                                                           *|
+|*                                                                           *|
+|*       Copyright 1993-1999 NVIDIA, Corporation.  All rights reserved.      *|
+|*                                                                           *|
+|*     NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY     *|
+|*     OF  THIS SOURCE  CODE  FOR ANY PURPOSE.  IT IS  PROVIDED  "AS IS"     *|
+|*     WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND.  NVIDIA, CORPOR-     *|
+|*     ATION DISCLAIMS ALL WARRANTIES  WITH REGARD  TO THIS SOURCE CODE,     *|
+|*     INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE-     *|
+|*     MENT,  AND FITNESS  FOR A PARTICULAR PURPOSE.   IN NO EVENT SHALL     *|
+|*     NVIDIA, CORPORATION  BE LIABLE FOR ANY SPECIAL,  INDIRECT,  INCI-     *|
+|*     DENTAL, OR CONSEQUENTIAL DAMAGES,  OR ANY DAMAGES  WHATSOEVER RE-     *|
+|*     SULTING FROM LOSS OF USE,  DATA OR PROFITS,  WHETHER IN AN ACTION     *|
+|*     OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,  ARISING OUT OF     *|
+|*     OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE.     *|
+|*                                                                           *|
+|*     U.S. Government  End  Users.   This source code  is a "commercial     *|
+|*     item,"  as that  term is  defined at  48 C.F.R. 2.101 (OCT 1995),     *|
+|*     consisting  of "commercial  computer  software"  and  "commercial     *|
+|*     computer  software  documentation,"  as such  terms  are  used in     *|
+|*     48 C.F.R. 12.212 (SEPT 1995)  and is provided to the U.S. Govern-     *|
+|*     ment only as  a commercial end item.   Consistent with  48 C.F.R.     *|
+|*     12.212 and  48 C.F.R. 227.7202-1 through  227.7202-4 (JUNE 1995),     *|
+|*     all U.S. Government End Users  acquire the source code  with only     *|
+|*     those rights set forth herein.                                        *|
+|*                                                                           *|
+ \***************************************************************************/
+
+/*
+ * GPL Licensing Note - According to Mark Vojkovich, author of the Xorg/
+ * XFree86 'nv' driver, this source code is provided under MIT-style licensing
+ * where the source code is provided "as is" without warranty of any kind.
+ * The only usage restriction is for the copyright notices to be retained
+ * whenever code is used.
+ *
+ * Antonino Daplas <adaplas@pol.net> 2005-03-11
+ */
+
+#ifndef __NV_LOCAL_H__
+#define __NV_LOCAL_H__
+
+/*
+ * This file includes any environment or machine specific values to access the
+ * HW.  Put all affected includes, typdefs, etc. here so the riva_hw.* files
+ * can stay generic in nature.
+ */
+
+/*
+ * HW access macros.  These assume memory-mapped I/O, and not normal I/O space.
+ */
+#define NV_WR08(p,i,d)  (__raw_writeb((d), (void __iomem *)(p) + (i)))
+#define NV_RD08(p,i)    (__raw_readb((void __iomem *)(p) + (i)))
+#define NV_WR16(p,i,d)  (__raw_writew((d), (void __iomem *)(p) + (i)))
+#define NV_RD16(p,i)    (__raw_readw((void __iomem *)(p) + (i)))
+#define NV_WR32(p,i,d)  (__raw_writel((d), (void __iomem *)(p) + (i)))
+#define NV_RD32(p,i)    (__raw_readl((void __iomem *)(p) + (i)))
+
+/* VGA I/O is now always done through MMIO */
+#define VGA_WR08(p,i,d) (writeb((d), (void __iomem *)(p) + (i)))
+#define VGA_RD08(p,i)   (readb((void __iomem *)(p) + (i)))
+
+#define NVDmaNext(par, data) \
+     NV_WR32(&(par)->dmaBase[(par)->dmaCurrent++], 0, (data))
+
+#define NVDmaStart(par, tag, size) {          \
+     if((par)->dmaFree <= (size))             \
+        NVDmaWait(par, size);                 \
+     NVDmaNext(par, ((size) << 18) | (tag));  \
+     (par)->dmaFree -= ((size) + 1);          \
+}
+
+#if defined(__i386__)
+#define _NV_FENCE() outb(0, 0x3D0);
+#else
+#define _NV_FENCE() mb();
+#endif
+
+#define WRITE_PUT(par, data) {                   \
+  _NV_FENCE()                                    \
+  NV_RD08((par)->FbStart, 0);                    \
+  NV_WR32(&(par)->FIFO[0x0010], 0, (data) << 2); \
+  mb();                                          \
+}
+
+#define READ_GET(par) (NV_RD32(&(par)->FIFO[0x0011], 0) >> 2)
+
+#define reverse_order(l)        \
+do {                            \
+       u8 *a = (u8 *)(l);      \
+       *a = byte_rev[*a], a++; \
+       *a = byte_rev[*a], a++; \
+       *a = byte_rev[*a], a++; \
+       *a = byte_rev[*a];      \
+} while(0)
+
+#endif                         /* __NV_LOCAL_H__ */
diff --git a/drivers/video/nvidia/nv_of.c b/drivers/video/nvidia/nv_of.c
new file mode 100644 (file)
index 0000000..7d12eb8
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * linux/drivers/video/nvidia/nv_of.c
+ *
+ * Copyright 2004 Antonino A. Daplas <adaplas @pol.net>
+ *
+ * Based 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 <asm/prom.h>
+#include <asm/pci-bridge.h>
+
+#include "nv_type.h"
+#include "nv_local.h"
+#include "nv_proto.h"
+
+void nvidia_create_i2c_busses(struct nvidia_par *par) {}
+void nvidia_delete_i2c_busses(struct nvidia_par *par) {}
+
+int nvidia_probe_i2c_connector(struct nvidia_par *par, int conn, u8 **out_edid)
+{
+       struct device_node *dp;
+       unsigned char *pedid = NULL;
+       unsigned char *disptype = NULL;
+       static char *propnames[] = {
+               "DFP,EDID", "LCD,EDID", "EDID", "EDID1", "EDID,B", "EDID,A", NULL };
+       int i;
+
+       dp = pci_device_to_OF_node(par->pci_dev);
+       for (; dp != NULL; dp = dp->child) {
+               disptype = (unsigned char *)get_property(dp, "display-type", NULL);
+               if (disptype == NULL)
+                       continue;
+               if (strncmp(disptype, "LCD", 3) != 0)
+                       continue;
+               for (i = 0; propnames[i] != NULL; ++i) {
+                       pedid = (unsigned char *)
+                               get_property(dp, propnames[i], NULL);
+                       if (pedid != NULL) {
+                               *out_edid = pedid;
+                               return 0;
+                       }
+               }
+       }
+       return 1;
+}
diff --git a/drivers/video/nvidia/nv_proto.h b/drivers/video/nvidia/nv_proto.h
new file mode 100644 (file)
index 0000000..42847ce
--- /dev/null
@@ -0,0 +1,58 @@
+/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nv/nv_proto.h,v 1.10 2003/07/31 20:24:29 mvojkovi Exp $ */
+
+#ifndef __NV_PROTO_H__
+#define __NV_PROTO_H__
+
+/* in nv_setup.c */
+void NVCommonSetup(struct fb_info *info);
+void NVWriteCrtc(struct nvidia_par *par, u8 index, u8 value);
+u8 NVReadCrtc(struct nvidia_par *par, u8 index);
+void NVWriteGr(struct nvidia_par *par, u8 index, u8 value);
+u8 NVReadGr(struct nvidia_par *par, u8 index);
+void NVWriteSeq(struct nvidia_par *par, u8 index, u8 value);
+u8 NVReadSeq(struct nvidia_par *par, u8 index);
+void NVWriteAttr(struct nvidia_par *par, u8 index, u8 value);
+u8 NVReadAttr(struct nvidia_par *par, u8 index);
+void NVWriteMiscOut(struct nvidia_par *par, u8 value);
+u8 NVReadMiscOut(struct nvidia_par *par);
+void NVWriteDacMask(struct nvidia_par *par, u8 value);
+void NVWriteDacReadAddr(struct nvidia_par *par, u8 value);
+void NVWriteDacWriteAddr(struct nvidia_par *par, u8 value);
+void NVWriteDacData(struct nvidia_par *par, u8 value);
+u8 NVReadDacData(struct nvidia_par *par);
+
+/* in nv_hw.c */
+void NVCalcStateExt(struct nvidia_par *par, struct _riva_hw_state *,
+                   int, int, int, int, int, int);
+void NVLoadStateExt(struct nvidia_par *par, struct _riva_hw_state *);
+void NVUnloadStateExt(struct nvidia_par *par, struct _riva_hw_state *);
+void NVSetStartAddress(struct nvidia_par *par, u32);
+int NVShowHideCursor(struct nvidia_par *par, int);
+void NVLockUnlock(struct nvidia_par *par, int);
+
+/* in nvidia-i2c.c */
+#if defined(CONFIG_FB_NVIDIA_I2C) || defined (CONFIG_PPC_OF)
+void nvidia_create_i2c_busses(struct nvidia_par *par);
+void nvidia_delete_i2c_busses(struct nvidia_par *par);
+int nvidia_probe_i2c_connector(struct nvidia_par *par, int conn,
+                              u8 ** out_edid);
+#else
+#define nvidia_create_i2c_busses(...)
+#define nvidia_delete_i2c_busses(...)
+#define nvidia_probe_i2c_connector(p, c, edid) \
+do {                                           \
+       *(edid) = NULL;                        \
+} while(0)
+#endif
+
+/* in nv_accel.c */
+extern void NVResetGraphics(struct fb_info *info);
+extern void nvidiafb_copyarea(struct fb_info *info,
+                             const struct fb_copyarea *region);
+extern void nvidiafb_fillrect(struct fb_info *info,
+                             const struct fb_fillrect *rect);
+extern void nvidiafb_imageblit(struct fb_info *info,
+                              const struct fb_image *image);
+extern int nvidiafb_sync(struct fb_info *info);
+extern u8 byte_rev[256];
+#endif                         /* __NV_PROTO_H__ */
diff --git a/drivers/video/nvidia/nv_setup.c b/drivers/video/nvidia/nv_setup.c
new file mode 100644 (file)
index 0000000..0bbdca2
--- /dev/null
@@ -0,0 +1,636 @@
+ /***************************************************************************\
+|*                                                                           *|
+|*       Copyright 2003 NVIDIA, Corporation.  All rights reserved.           *|
+|*                                                                           *|
+|*     NOTICE TO USER:   The source code  is copyrighted under  U.S. and     *|
+|*     international laws.  Users and possessors of this source code are     *|
+|*     hereby granted a nonexclusive,  royalty-free copyright license to     *|
+|*     use this code in individual and commercial software.                  *|
+|*                                                                           *|
+|*     Any use of this source code must include,  in the user documenta-     *|
+|*     tion and  internal comments to the code,  notices to the end user     *|
+|*     as follows:                                                           *|
+|*                                                                           *|
+|*       Copyright 2003 NVIDIA, Corporation.  All rights reserved.           *|
+|*                                                                           *|
+|*     NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY     *|
+|*     OF  THIS SOURCE  CODE  FOR ANY PURPOSE.  IT IS  PROVIDED  "AS IS"     *|
+|*     WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND.  NVIDIA, CORPOR-     *|
+|*     ATION DISCLAIMS ALL WARRANTIES  WITH REGARD  TO THIS SOURCE CODE,     *|
+|*     INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE-     *|
+|*     MENT,  AND FITNESS  FOR A PARTICULAR PURPOSE.   IN NO EVENT SHALL     *|
+|*     NVIDIA, CORPORATION  BE LIABLE FOR ANY SPECIAL,  INDIRECT,  INCI-     *|
+|*     DENTAL, OR CONSEQUENTIAL DAMAGES,  OR ANY DAMAGES  WHATSOEVER RE-     *|
+|*     SULTING FROM LOSS OF USE,  DATA OR PROFITS,  WHETHER IN AN ACTION     *|
+|*     OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,  ARISING OUT OF     *|
+|*     OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE.     *|
+|*                                                                           *|
+|*     U.S. Government  End  Users.   This source code  is a "commercial     *|
+|*     item,"  as that  term is  defined at  48 C.F.R. 2.101 (OCT 1995),     *|
+|*     consisting  of "commercial  computer  software"  and  "commercial     *|
+|*     computer  software  documentation,"  as such  terms  are  used in     *|
+|*     48 C.F.R. 12.212 (SEPT 1995)  and is provided to the U.S. Govern-     *|
+|*     ment only as  a commercial end item.   Consistent with  48 C.F.R.     *|
+|*     12.212 and  48 C.F.R. 227.7202-1 through  227.7202-4 (JUNE 1995),     *|
+|*     all U.S. Government End Users  acquire the source code  with only     *|
+|*     those rights set forth herein.                                        *|
+|*                                                                           *|
+ \***************************************************************************/
+
+/*
+ * GPL Licensing Note - According to Mark Vojkovich, author of the Xorg/
+ * XFree86 'nv' driver, this source code is provided under MIT-style licensing
+ * where the source code is provided "as is" without warranty of any kind.
+ * The only usage restriction is for the copyright notices to be retained
+ * whenever code is used.
+ *
+ * Antonino Daplas <adaplas@pol.net> 2005-03-11
+ */
+
+#include <video/vga.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include "nv_type.h"
+#include "nv_local.h"
+#include "nv_proto.h"
+/*
+ * Override VGA I/O routines.
+ */
+void NVWriteCrtc(struct nvidia_par *par, u8 index, u8 value)
+{
+       VGA_WR08(par->PCIO, par->IOBase + 0x04, index);
+       VGA_WR08(par->PCIO, par->IOBase + 0x05, value);
+}
+u8 NVReadCrtc(struct nvidia_par *par, u8 index)
+{
+       VGA_WR08(par->PCIO, par->IOBase + 0x04, index);
+       return (VGA_RD08(par->PCIO, par->IOBase + 0x05));
+}
+void NVWriteGr(struct nvidia_par *par, u8 index, u8 value)
+{
+       VGA_WR08(par->PVIO, VGA_GFX_I, index);
+       VGA_WR08(par->PVIO, VGA_GFX_D, value);
+}
+u8 NVReadGr(struct nvidia_par *par, u8 index)
+{
+       VGA_WR08(par->PVIO, VGA_GFX_I, index);
+       return (VGA_RD08(par->PVIO, VGA_GFX_D));
+}
+void NVWriteSeq(struct nvidia_par *par, u8 index, u8 value)
+{
+       VGA_WR08(par->PVIO, VGA_SEQ_I, index);
+       VGA_WR08(par->PVIO, VGA_SEQ_D, value);
+}
+u8 NVReadSeq(struct nvidia_par *par, u8 index)
+{
+       VGA_WR08(par->PVIO, VGA_SEQ_I, index);
+       return (VGA_RD08(par->PVIO, VGA_SEQ_D));
+}
+void NVWriteAttr(struct nvidia_par *par, u8 index, u8 value)
+{
+       volatile u8 tmp;
+
+       tmp = VGA_RD08(par->PCIO, par->IOBase + 0x0a);
+       if (par->paletteEnabled)
+               index &= ~0x20;
+       else
+               index |= 0x20;
+       VGA_WR08(par->PCIO, VGA_ATT_IW, index);
+       VGA_WR08(par->PCIO, VGA_ATT_W, value);
+}
+u8 NVReadAttr(struct nvidia_par *par, u8 index)
+{
+       volatile u8 tmp;
+
+       tmp = VGA_RD08(par->PCIO, par->IOBase + 0x0a);
+       if (par->paletteEnabled)
+               index &= ~0x20;
+       else
+               index |= 0x20;
+       VGA_WR08(par->PCIO, VGA_ATT_IW, index);
+       return (VGA_RD08(par->PCIO, VGA_ATT_R));
+}
+void NVWriteMiscOut(struct nvidia_par *par, u8 value)
+{
+       VGA_WR08(par->PVIO, VGA_MIS_W, value);
+}
+u8 NVReadMiscOut(struct nvidia_par *par)
+{
+       return (VGA_RD08(par->PVIO, VGA_MIS_R));
+}
+#if 0
+void NVEnablePalette(struct nvidia_par *par)
+{
+       volatile u8 tmp;
+
+       tmp = VGA_RD08(par->PCIO, par->IOBase + 0x0a);
+       VGA_WR08(par->PCIO, VGA_ATT_IW, 0x00);
+       par->paletteEnabled = 1;
+}
+void NVDisablePalette(struct nvidia_par *par)
+{
+       volatile u8 tmp;
+
+       tmp = VGA_RD08(par->PCIO, par->IOBase + 0x0a);
+       VGA_WR08(par->PCIO, VGA_ATT_IW, 0x20);
+       par->paletteEnabled = 0;
+}
+#endif  /*  0  */
+void NVWriteDacMask(struct nvidia_par *par, u8 value)
+{
+       VGA_WR08(par->PDIO, VGA_PEL_MSK, value);
+}
+#if 0
+u8 NVReadDacMask(struct nvidia_par *par)
+{
+       return (VGA_RD08(par->PDIO, VGA_PEL_MSK));
+}
+#endif  /*  0  */
+void NVWriteDacReadAddr(struct nvidia_par *par, u8 value)
+{
+       VGA_WR08(par->PDIO, VGA_PEL_IR, value);
+}
+void NVWriteDacWriteAddr(struct nvidia_par *par, u8 value)
+{
+       VGA_WR08(par->PDIO, VGA_PEL_IW, value);
+}
+void NVWriteDacData(struct nvidia_par *par, u8 value)
+{
+       VGA_WR08(par->PDIO, VGA_PEL_D, value);
+}
+u8 NVReadDacData(struct nvidia_par *par)
+{
+       return (VGA_RD08(par->PDIO, VGA_PEL_D));
+}
+
+static int NVIsConnected(struct nvidia_par *par, int output)
+{
+       volatile u32 __iomem *PRAMDAC = par->PRAMDAC0;
+       u32 reg52C, reg608;
+       int present;
+
+       if (output)
+               PRAMDAC += 0x800;
+
+       reg52C = NV_RD32(PRAMDAC, 0x052C);
+       reg608 = NV_RD32(PRAMDAC, 0x0608);
+
+       NV_WR32(PRAMDAC, 0x0608, reg608 & ~0x00010000);
+
+       NV_WR32(PRAMDAC, 0x052C, reg52C & 0x0000FEEE);
+       msleep(1);
+       NV_WR32(PRAMDAC, 0x052C, NV_RD32(PRAMDAC, 0x052C) | 1);
+
+       NV_WR32(par->PRAMDAC0, 0x0610, 0x94050140);
+       NV_WR32(par->PRAMDAC0, 0x0608, NV_RD32(par->PRAMDAC0, 0x0608) |
+               0x00001000);
+
+       msleep(1);
+
+       present = (NV_RD32(PRAMDAC, 0x0608) & (1 << 28)) ? 1 : 0;
+
+       if (present)
+               printk("nvidiafb: CRTC%i found\n", output);
+       else
+               printk("nvidiafb: CRTC%i not found\n", output);
+
+       NV_WR32(par->PRAMDAC0, 0x0608, NV_RD32(par->PRAMDAC0, 0x0608) &
+               0x0000EFFF);
+
+       NV_WR32(PRAMDAC, 0x052C, reg52C);
+       NV_WR32(PRAMDAC, 0x0608, reg608);
+
+       return present;
+}
+
+static void NVSelectHeadRegisters(struct nvidia_par *par, int head)
+{
+       if (head) {
+               par->PCIO = par->PCIO0 + 0x2000;
+               par->PCRTC = par->PCRTC0 + 0x800;
+               par->PRAMDAC = par->PRAMDAC0 + 0x800;
+               par->PDIO = par->PDIO0 + 0x2000;
+       } else {
+               par->PCIO = par->PCIO0;
+               par->PCRTC = par->PCRTC0;
+               par->PRAMDAC = par->PRAMDAC0;
+               par->PDIO = par->PDIO0;
+       }
+}
+
+static void nv4GetConfig(struct nvidia_par *par)
+{
+       if (NV_RD32(par->PFB, 0x0000) & 0x00000100) {
+               par->RamAmountKBytes =
+                   ((NV_RD32(par->PFB, 0x0000) >> 12) & 0x0F) * 1024 * 2 +
+                   1024 * 2;
+       } else {
+               switch (NV_RD32(par->PFB, 0x0000) & 0x00000003) {
+               case 0:
+                       par->RamAmountKBytes = 1024 * 32;
+                       break;
+               case 1:
+                       par->RamAmountKBytes = 1024 * 4;
+                       break;
+               case 2:
+                       par->RamAmountKBytes = 1024 * 8;
+                       break;
+               case 3:
+               default:
+                       par->RamAmountKBytes = 1024 * 16;
+                       break;
+               }
+       }
+       par->CrystalFreqKHz = (NV_RD32(par->PEXTDEV, 0x0000) & 0x00000040) ?
+           14318 : 13500;
+       par->CURSOR = &par->PRAMIN[0x1E00];
+       par->MinVClockFreqKHz = 12000;
+       par->MaxVClockFreqKHz = 350000;
+}
+
+static void nv10GetConfig(struct nvidia_par *par)
+{
+       struct pci_dev *dev;
+       u32 implementation = par->Chipset & 0x0ff0;
+
+#ifdef __BIG_ENDIAN
+       /* turn on big endian register access */
+       if (!(NV_RD32(par->PMC, 0x0004) & 0x01000001)) {
+               NV_WR32(par->PMC, 0x0004, 0x01000001);
+               mb();
+       }
+#endif
+
+       dev = pci_find_slot(0, 1);
+       if ((par->Chipset && 0xffff) == 0x01a0) {
+               int amt = 0;
+
+               pci_read_config_dword(dev, 0x7c, &amt);
+               par->RamAmountKBytes = (((amt >> 6) & 31) + 1) * 1024;
+       } else if ((par->Chipset & 0xffff) == 0x01f0) {
+               int amt = 0;
+
+               pci_read_config_dword(dev, 0x84, &amt);
+               par->RamAmountKBytes = (((amt >> 4) & 127) + 1) * 1024;
+       } else {
+               par->RamAmountKBytes =
+                   (NV_RD32(par->PFB, 0x020C) & 0xFFF00000) >> 10;
+       }
+
+       par->CrystalFreqKHz = (NV_RD32(par->PEXTDEV, 0x0000) & (1 << 6)) ?
+           14318 : 13500;
+
+       if (par->twoHeads && (implementation != 0x0110)) {
+               if (NV_RD32(par->PEXTDEV, 0x0000) & (1 << 22))
+                       par->CrystalFreqKHz = 27000;
+       }
+
+       par->CursorStart = (par->RamAmountKBytes - 96) * 1024;
+       par->CURSOR = NULL;     /* can't set this here */
+       par->MinVClockFreqKHz = 12000;
+       par->MaxVClockFreqKHz = par->twoStagePLL ? 400000 : 350000;
+}
+
+void NVCommonSetup(struct fb_info *info)
+{
+       struct nvidia_par *par = info->par;
+       struct fb_var_screeninfo var;
+       u16 implementation = par->Chipset & 0x0ff0;
+       u8 *edidA = NULL, *edidB = NULL;
+       struct fb_monspecs monitorA, monitorB;
+       struct fb_monspecs *monA = NULL, *monB = NULL;
+       int mobile = 0;
+       int tvA = 0;
+       int tvB = 0;
+       int FlatPanel = -1;     /* really means the CRTC is slaved */
+       int Television = 0;
+
+       par->PRAMIN = par->REGS + (0x00710000 / 4);
+       par->PCRTC0 = par->REGS + (0x00600000 / 4);
+       par->PRAMDAC0 = par->REGS + (0x00680000 / 4);
+       par->PFB = par->REGS + (0x00100000 / 4);
+       par->PFIFO = par->REGS + (0x00002000 / 4);
+       par->PGRAPH = par->REGS + (0x00400000 / 4);
+       par->PEXTDEV = par->REGS + (0x00101000 / 4);
+       par->PTIMER = par->REGS + (0x00009000 / 4);
+       par->PMC = par->REGS + (0x00000000 / 4);
+       par->FIFO = par->REGS + (0x00800000 / 4);
+
+       /* 8 bit registers */
+       par->PCIO0 = (u8 __iomem *) par->REGS + 0x00601000;
+       par->PDIO0 = (u8 __iomem *) par->REGS + 0x00681000;
+       par->PVIO = (u8 __iomem *) par->REGS + 0x000C0000;
+
+       par->twoHeads = (par->Architecture >= NV_ARCH_10) &&
+           (implementation != 0x0100) &&
+           (implementation != 0x0150) &&
+           (implementation != 0x01A0) && (implementation != 0x0200);
+
+       par->fpScaler = (par->FpScale && par->twoHeads &&
+                        (implementation != 0x0110));
+
+       par->twoStagePLL = (implementation == 0x0310) ||
+           (implementation == 0x0340) || (par->Architecture >= NV_ARCH_40);
+
+       par->WaitVSyncPossible = (par->Architecture >= NV_ARCH_10) &&
+           (implementation != 0x0100);
+
+       par->BlendingPossible = ((par->Chipset & 0xffff) != 0x0020);
+
+       /* look for known laptop chips */
+       switch (par->Chipset & 0xffff) {
+       case 0x0112:
+       case 0x0174:
+       case 0x0175:
+       case 0x0176:
+       case 0x0177:
+       case 0x0179:
+       case 0x017C:
+       case 0x017D:
+       case 0x0186:
+       case 0x0187:
+       case 0x018D:
+       case 0x0286:
+       case 0x028C:
+       case 0x0316:
+       case 0x0317:
+       case 0x031A:
+       case 0x031B:
+       case 0x031C:
+       case 0x031D:
+       case 0x031E:
+       case 0x031F:
+       case 0x0324:
+       case 0x0325:
+       case 0x0328:
+       case 0x0329:
+       case 0x032C:
+       case 0x032D:
+       case 0x0347:
+       case 0x0348:
+       case 0x0349:
+       case 0x034B:
+       case 0x034C:
+       case 0x0160:
+       case 0x0166:
+       case 0x00C8:
+       case 0x00CC:
+       case 0x0144:
+       case 0x0146:
+       case 0x0147:
+       case 0x0148:
+               mobile = 1;
+               break;
+       default:
+               break;
+       }
+
+       if (par->Architecture == NV_ARCH_04)
+               nv4GetConfig(par);
+       else
+               nv10GetConfig(par);
+
+       NVSelectHeadRegisters(par, 0);
+
+       NVLockUnlock(par, 0);
+
+       par->IOBase = (NVReadMiscOut(par) & 0x01) ? 0x3d0 : 0x3b0;
+
+       par->Television = 0;
+
+       nvidia_create_i2c_busses(par);
+       if (!par->twoHeads) {
+               par->CRTCnumber = 0;
+               nvidia_probe_i2c_connector(par, 1, &edidA);
+               if (edidA && !fb_parse_edid(edidA, &var)) {
+                       printk("nvidiafb: EDID found from BUS1\n");
+                       monA = &monitorA;
+                       fb_edid_to_monspecs(edidA, monA);
+                       FlatPanel = (monA->input & FB_DISP_DDI) ? 1 : 0;
+
+                       /* NV4 doesn't support FlatPanels */
+                       if ((par->Chipset & 0x0fff) <= 0x0020)
+                               FlatPanel = 0;
+               } else {
+                       VGA_WR08(par->PCIO, 0x03D4, 0x28);
+                       if (VGA_RD08(par->PCIO, 0x03D5) & 0x80) {
+                               VGA_WR08(par->PCIO, 0x03D4, 0x33);
+                               if (!(VGA_RD08(par->PCIO, 0x03D5) & 0x01))
+                                       Television = 1;
+                               FlatPanel = 1;
+                       } else {
+                               FlatPanel = 0;
+                       }
+                       printk("nvidiafb: HW is currently programmed for %s\n",
+                              FlatPanel ? (Television ? "TV" : "DFP") :
+                              "CRT");
+               }
+
+               if (par->FlatPanel == -1) {
+                       par->FlatPanel = FlatPanel;
+                       par->Television = Television;
+               } else {
+                       printk("nvidiafb: Forcing display type to %s as "
+                              "specified\n", par->FlatPanel ? "DFP" : "CRT");
+               }
+       } else {
+               u8 outputAfromCRTC, outputBfromCRTC;
+               int CRTCnumber = -1;
+               u8 slaved_on_A, slaved_on_B;
+               int analog_on_A, analog_on_B;
+               u32 oldhead;
+               u8 cr44;
+
+               if (implementation != 0x0110) {
+                       if (NV_RD32(par->PRAMDAC0, 0x0000052C) & 0x100)
+                               outputAfromCRTC = 1;
+                       else
+                               outputAfromCRTC = 0;
+                       if (NV_RD32(par->PRAMDAC0, 0x0000252C) & 0x100)
+                               outputBfromCRTC = 1;
+                       else
+                               outputBfromCRTC = 0;
+                       analog_on_A = NVIsConnected(par, 0);
+                       analog_on_B = NVIsConnected(par, 1);
+               } else {
+                       outputAfromCRTC = 0;
+                       outputBfromCRTC = 1;
+                       analog_on_A = 0;
+                       analog_on_B = 0;
+               }
+
+               VGA_WR08(par->PCIO, 0x03D4, 0x44);
+               cr44 = VGA_RD08(par->PCIO, 0x03D5);
+
+               VGA_WR08(par->PCIO, 0x03D5, 3);
+               NVSelectHeadRegisters(par, 1);
+               NVLockUnlock(par, 0);
+
+               VGA_WR08(par->PCIO, 0x03D4, 0x28);
+               slaved_on_B = VGA_RD08(par->PCIO, 0x03D5) & 0x80;
+               if (slaved_on_B) {
+                       VGA_WR08(par->PCIO, 0x03D4, 0x33);
+                       tvB = !(VGA_RD08(par->PCIO, 0x03D5) & 0x01);
+               }
+
+               VGA_WR08(par->PCIO, 0x03D4, 0x44);
+               VGA_WR08(par->PCIO, 0x03D5, 0);
+               NVSelectHeadRegisters(par, 0);
+               NVLockUnlock(par, 0);
+
+               VGA_WR08(par->PCIO, 0x03D4, 0x28);
+               slaved_on_A = VGA_RD08(par->PCIO, 0x03D5) & 0x80;
+               if (slaved_on_A) {
+                       VGA_WR08(par->PCIO, 0x03D4, 0x33);
+                       tvA = !(VGA_RD08(par->PCIO, 0x03D5) & 0x01);
+               }
+
+               oldhead = NV_RD32(par->PCRTC0, 0x00000860);
+               NV_WR32(par->PCRTC0, 0x00000860, oldhead | 0x00000010);
+
+               nvidia_probe_i2c_connector(par, 1, &edidA);
+               if (edidA && !fb_parse_edid(edidA, &var)) {
+                       printk("nvidiafb: EDID found from BUS1\n");
+                       monA = &monitorA;
+                       fb_edid_to_monspecs(edidA, monA);
+               }
+
+               nvidia_probe_i2c_connector(par, 2, &edidB);
+               if (edidB && !fb_parse_edid(edidB, &var)) {
+                       printk("nvidiafb: EDID found from BUS2\n");
+                       monB = &monitorB;
+                       fb_edid_to_monspecs(edidB, monB);
+               }
+
+               if (slaved_on_A && !tvA) {
+                       CRTCnumber = 0;
+                       FlatPanel = 1;
+                       printk("nvidiafb: CRTC 0 is currently programmed for "
+                              "DFP\n");
+               } else if (slaved_on_B && !tvB) {
+                       CRTCnumber = 1;
+                       FlatPanel = 1;
+                       printk("nvidiafb: CRTC 1 is currently programmed "
+                              "for DFP\n");
+               } else if (analog_on_A) {
+                       CRTCnumber = outputAfromCRTC;
+                       FlatPanel = 0;
+                       printk("nvidiafb: CRTC %i appears to have a "
+                              "CRT attached\n", CRTCnumber);
+               } else if (analog_on_B) {
+                       CRTCnumber = outputBfromCRTC;
+                       FlatPanel = 0;
+                       printk("nvidiafb: CRTC %i"
+                              "appears to have a "
+                              "CRT attached\n", CRTCnumber);
+               } else if (slaved_on_A) {
+                       CRTCnumber = 0;
+                       FlatPanel = 1;
+                       Television = 1;
+                       printk("nvidiafb: CRTC 0 is currently programmed "
+                              "for TV\n");
+               } else if (slaved_on_B) {
+                       CRTCnumber = 1;
+                       FlatPanel = 1;
+                       Television = 1;
+                       printk("nvidiafb: CRTC 1 is currently programmed for "
+                              "TV\n");
+               } else if (monA) {
+                       FlatPanel = (monA->input & FB_DISP_DDI) ? 1 : 0;
+               } else if (monB) {
+                       FlatPanel = (monB->input & FB_DISP_DDI) ? 1 : 0;
+               }
+
+               if (par->FlatPanel == -1) {
+                       if (FlatPanel != -1) {
+                               par->FlatPanel = FlatPanel;
+                               par->Television = Television;
+                       } else {
+                               printk("nvidiafb: Unable to detect display "
+                                      "type...\n");
+                               if (mobile) {
+                                       printk("...On a laptop, assuming "
+                                              "DFP\n");
+                                       par->FlatPanel = 1;
+                               } else {
+                                       printk("...Using default of CRT\n");
+                                       par->FlatPanel = 0;
+                               }
+                       }
+               } else {
+                       printk("nvidiafb: Forcing display type to %s as "
+                              "specified\n", par->FlatPanel ? "DFP" : "CRT");
+               }
+
+               if (par->CRTCnumber == -1) {
+                       if (CRTCnumber != -1)
+                               par->CRTCnumber = CRTCnumber;
+                       else {
+                               printk("nvidiafb: Unable to detect which "
+                                      "CRTCNumber...\n");
+                               if (par->FlatPanel)
+                                       par->CRTCnumber = 1;
+                               else
+                                       par->CRTCnumber = 0;
+                               printk("...Defaulting to CRTCNumber %i\n",
+                                      par->CRTCnumber);
+                       }
+               } else {
+                       printk("nvidiafb: Forcing CRTCNumber %i as "
+                              "specified\n", par->CRTCnumber);
+               }
+
+               if (monA) {
+                       if (((monA->input & FB_DISP_DDI) &&
+                            par->FlatPanel) ||
+                           ((!(monA->input & FB_DISP_DDI)) &&
+                            !par->FlatPanel)) {
+                               if (monB) {
+                                       fb_destroy_modedb(monB->modedb);
+                                       monB = NULL;
+                               }
+                       } else {
+                               fb_destroy_modedb(monA->modedb);
+                               monA = NULL;
+                       }
+               }
+
+               if (monB) {
+                       if (((monB->input & FB_DISP_DDI) &&
+                            !par->FlatPanel) ||
+                           ((!(monB->input & FB_DISP_DDI)) &&
+                            par->FlatPanel)) {
+                               fb_destroy_modedb(monB->modedb);
+                               monB = NULL;
+                       } else
+                               monA = monB;
+               }
+
+               if (implementation == 0x0110)
+                       cr44 = par->CRTCnumber * 0x3;
+
+               NV_WR32(par->PCRTC0, 0x00000860, oldhead);
+
+               VGA_WR08(par->PCIO, 0x03D4, 0x44);
+               VGA_WR08(par->PCIO, 0x03D5, cr44);
+               NVSelectHeadRegisters(par, par->CRTCnumber);
+       }
+
+       printk("nvidiafb: Using %s on CRTC %i\n",
+              par->FlatPanel ? (par->Television ? "TV" : "DFP") : "CRT",
+              par->CRTCnumber);
+
+       if (par->FlatPanel && !par->Television) {
+               par->fpWidth = NV_RD32(par->PRAMDAC, 0x0820) + 1;
+               par->fpHeight = NV_RD32(par->PRAMDAC, 0x0800) + 1;
+               par->fpSyncs = NV_RD32(par->PRAMDAC, 0x0848) & 0x30000033;
+
+               printk("Panel size is %i x %i\n", par->fpWidth, par->fpHeight);
+       }
+
+       if (monA)
+               info->monspecs = *monA;
+
+       kfree(edidA);
+       kfree(edidB);
+}
diff --git a/drivers/video/nvidia/nv_type.h b/drivers/video/nvidia/nv_type.h
new file mode 100644 (file)
index 0000000..e4a5b1d
--- /dev/null
@@ -0,0 +1,174 @@
+#ifndef __NV_TYPE_H__
+#define __NV_TYPE_H__
+
+#include <linux/fb.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+
+#define NV_ARCH_04  0x04
+#define NV_ARCH_10  0x10
+#define NV_ARCH_20  0x20
+#define NV_ARCH_30  0x30
+#define NV_ARCH_40  0x40
+
+#define BITMASK(t,b) (((unsigned)(1U << (((t)-(b)+1)))-1)  << (b))
+#define MASKEXPAND(mask) BITMASK(1?mask,0?mask)
+#define SetBF(mask,value) ((value) << (0?mask))
+#define GetBF(var,mask) (((unsigned)((var) & MASKEXPAND(mask))) >> (0?mask) )
+#define SetBitField(value,from,to) SetBF(to, GetBF(value,from))
+#define SetBit(n) (1<<(n))
+#define Set8Bits(value) ((value)&0xff)
+
+#define V_DBLSCAN  1
+
+typedef struct {
+       int bitsPerPixel;
+       int depth;
+       int displayWidth;
+       int weight;
+} NVFBLayout;
+
+#define NUM_SEQ_REGS           0x05
+#define NUM_CRT_REGS           0x41
+#define NUM_GRC_REGS           0x09
+#define NUM_ATC_REGS           0x15
+
+struct nvidia_par;
+
+struct nvidia_i2c_chan {
+       struct nvidia_par *par;
+       unsigned long ddc_base;
+       struct i2c_adapter adapter;
+       struct i2c_algo_bit_data algo;
+};
+
+typedef struct _riva_hw_state {
+       u8 attr[NUM_ATC_REGS];
+       u8 crtc[NUM_CRT_REGS];
+       u8 gra[NUM_GRC_REGS];
+       u8 seq[NUM_SEQ_REGS];
+       u8 misc_output;
+       u32 bpp;
+       u32 width;
+       u32 height;
+       u32 interlace;
+       u32 repaint0;
+       u32 repaint1;
+       u32 screen;
+       u32 scale;
+       u32 dither;
+       u32 extra;
+       u32 fifo;
+       u32 pixel;
+       u32 horiz;
+       u32 arbitration0;
+       u32 arbitration1;
+       u32 pll;
+       u32 pllB;
+       u32 vpll;
+       u32 vpll2;
+       u32 vpllB;
+       u32 vpll2B;
+       u32 pllsel;
+       u32 general;
+       u32 crtcOwner;
+       u32 head;
+       u32 head2;
+       u32 config;
+       u32 cursorConfig;
+       u32 cursor0;
+       u32 cursor1;
+       u32 cursor2;
+       u32 timingH;
+       u32 timingV;
+       u32 displayV;
+       u32 crtcSync;
+} RIVA_HW_STATE;
+
+struct riva_regs {
+       RIVA_HW_STATE ext;
+};
+
+struct nvidia_par {
+       RIVA_HW_STATE SavedReg;
+       RIVA_HW_STATE ModeReg;
+       RIVA_HW_STATE *CurrentState;
+       u32 pseudo_palette[16];
+       struct pci_dev *pci_dev;
+       u32 Architecture;
+       u32 CursorStart;
+       int Chipset;
+       int bus;
+       unsigned long FbAddress;
+       u8 __iomem *FbStart;
+       u32 FbMapSize;
+       u32 FbUsableSize;
+       u32 ScratchBufferSize;
+       u32 ScratchBufferStart;
+       int FpScale;
+       u32 MinVClockFreqKHz;
+       u32 MaxVClockFreqKHz;
+       u32 CrystalFreqKHz;
+       u32 RamAmountKBytes;
+       u32 IOBase;
+       NVFBLayout CurrentLayout;
+       int cursor_reset;
+       int lockup;
+       int videoKey;
+       int FlatPanel;
+       int FPDither;
+       int Television;
+       int CRTCnumber;
+       int alphaCursor;
+       int twoHeads;
+       int twoStagePLL;
+       int fpScaler;
+       int fpWidth;
+       int fpHeight;
+       int PanelTweak;
+       int paneltweak;
+       u32 crtcSync_read;
+       u32 fpSyncs;
+       u32 dmaPut;
+       u32 dmaCurrent;
+       u32 dmaFree;
+       u32 dmaMax;
+       u32 __iomem *dmaBase;
+       u32 currentRop;
+       int WaitVSyncPossible;
+       int BlendingPossible;
+       u32 paletteEnabled;
+       u32 forceCRTC;
+       u8 DDCBase;
+#ifdef CONFIG_MTRR
+       struct {
+               int vram;
+               int vram_valid;
+       } mtrr;
+#endif
+       struct nvidia_i2c_chan chan[3];
+
+       volatile u32 __iomem *REGS;
+       volatile u32 __iomem *PCRTC0;
+       volatile u32 __iomem *PCRTC;
+       volatile u32 __iomem *PRAMDAC0;
+       volatile u32 __iomem *PFB;
+       volatile u32 __iomem *PFIFO;
+       volatile u32 __iomem *PGRAPH;
+       volatile u32 __iomem *PEXTDEV;
+       volatile u32 __iomem *PTIMER;
+       volatile u32 __iomem *PMC;
+       volatile u32 __iomem *PRAMIN;
+       volatile u32 __iomem *FIFO;
+       volatile u32 __iomem *CURSOR;
+       volatile u8 __iomem *PCIO0;
+       volatile u8 __iomem *PCIO;
+       volatile u8 __iomem *PVIO;
+       volatile u8 __iomem *PDIO0;
+       volatile u8 __iomem *PDIO;
+       volatile u32 __iomem *PRAMDAC;
+};
+
+#endif                         /* __NV_TYPE_H__ */
diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c
new file mode 100644 (file)
index 0000000..47733f5
--- /dev/null
@@ -0,0 +1,1745 @@
+/*
+ * linux/drivers/video/nvidia/nvidia.c - nVidia fb driver
+ *
+ * Copyright 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/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>
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+#ifdef CONFIG_PPC_OF
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#endif
+#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/backlight.h>
+#endif
+
+#include "nv_local.h"
+#include "nv_type.h"
+#include "nv_proto.h"
+#include "nv_dma.h"
+
+#ifndef CONFIG_PCI             /* sanity check */
+#error This driver requires PCI support.
+#endif
+
+#undef CONFIG_FB_NVIDIA_DEBUG
+#ifdef CONFIG_FB_NVIDIA_DEBUG
+#define NVTRACE          printk
+#else
+#define NVTRACE          if (0) printk
+#endif
+
+#define NVTRACE_ENTER(...)  NVTRACE("%s START\n", __FUNCTION__)
+#define NVTRACE_LEAVE(...)  NVTRACE("%s END\n", __FUNCTION__)
+
+#ifdef CONFIG_FB_NVIDIA_DEBUG
+#define assert(expr) \
+       if (!(expr)) { \
+       printk( "Assertion failed! %s,%s,%s,line=%d\n",\
+       #expr,__FILE__,__FUNCTION__,__LINE__); \
+       BUG(); \
+       }
+#else
+#define assert(expr)
+#endif
+
+#define PFX "nvidiafb: "
+
+/* HW cursor parameters */
+#define MAX_CURS               32
+
+static struct pci_device_id nvidiafb_pci_tbl[] = {
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TNT,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TNT2,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_UTNT2,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TNT_UNKNOWN,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_VTNT2,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_UVTNT2,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_ITNT2,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX2,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO2_MXR,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS2,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_ULTRA,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_460,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_SE,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_460_GO,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO_M32,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_500XGL,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO_M64,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_200,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_550XGL,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_500_GOGL,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_410_GO_M16,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_8X,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440SE_8X,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420_8X,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_448_GO,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_488_GO,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_580_XGL,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_MAC,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_280_NVS,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_380_XGL,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_IGEFORCE2,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3_1,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3_2,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_DDC,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4600,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4400,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4200,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800_8X,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800SE,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_4200_GO,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_980_XGL,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_780_XGL,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_700_GOGL,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800_ULTRA,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_2000,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1000,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600_ULTRA,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600SE,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5600,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5650,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO700,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_ULTRA,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_1,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200SE,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5200,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250_32,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO_5200,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_NVS_280_PCI,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_500,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5300,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5100,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900_ULTRA,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900XT,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5950_ULTRA,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_3000,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700_ULTRA,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700LE,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700VE,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_1,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_2,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO1000,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1100,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5500,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5100,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_700,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900ZT,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_ULTRA,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6800,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_LE,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_GT,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_4000,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6600_GT,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6600,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6610_XL,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_540,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6200,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0252,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0313,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0316,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0317,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x031D,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x031E,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x031F,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0329,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x032F,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0345,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0349,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x034B,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x034F,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x00c0,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_GEFORCE_6800A,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_GEFORCE_6800A_LE,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_GEFORCE_GO_6800,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_GEFORCE_GO_6800_ULTRA,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_QUADRO_FX_GO1400,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x00cd,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_QUADRO_FX_1400,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0142,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0143,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0144,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0145,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0146,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0147,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0148,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0149,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x014b,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x14c,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x014d,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0160,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6200_TURBOCACHE,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0162,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0163,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0165,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200_1,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250_1,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0169,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x016b,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x016c,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x016d,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x016e,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0210,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_LE,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_GT,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x021d,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x021e,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0220,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0221,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0222,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_NVIDIA, 0x0228,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0,}                    /* terminate list */
+};
+
+MODULE_DEVICE_TABLE(pci, nvidiafb_pci_tbl);
+
+/* command line data, set in nvidiafb_setup() */
+static int flatpanel __devinitdata = -1;       /* Autodetect later */
+static int forceCRTC __devinitdata = -1;
+static int hwcur __devinitdata = 0;
+static int noaccel __devinitdata = 0;
+static int noscale __devinitdata = 0;
+static int paneltweak __devinitdata = 0;
+static int vram __devinitdata = 0;
+#ifdef CONFIG_MTRR
+static int nomtrr __devinitdata = 0;
+#endif
+
+static char *mode_option __devinitdata = NULL;
+
+static struct fb_fix_screeninfo __devinitdata nvidiafb_fix = {
+       .type = FB_TYPE_PACKED_PIXELS,
+       .xpanstep = 8,
+       .ypanstep = 1,
+};
+
+static struct fb_var_screeninfo __devinitdata nvidiafb_default_var = {
+       .xres = 640,
+       .yres = 480,
+       .xres_virtual = 640,
+       .yres_virtual = 480,
+       .bits_per_pixel = 8,
+       .red = {0, 8, 0},
+       .green = {0, 8, 0},
+       .blue = {0, 8, 0},
+       .transp = {0, 0, 0},
+       .activate = FB_ACTIVATE_NOW,
+       .height = -1,
+       .width = -1,
+       .pixclock = 39721,
+       .left_margin = 40,
+       .right_margin = 24,
+       .upper_margin = 32,
+       .lower_margin = 11,
+       .hsync_len = 96,
+       .vsync_len = 2,
+       .vmode = FB_VMODE_NONINTERLACED
+};
+
+/*
+ * Backlight control
+ */
+#ifdef CONFIG_PMAC_BACKLIGHT
+
+static int nvidia_backlight_levels[] = {
+       0x158,
+       0x192,
+       0x1c6,
+       0x200,
+       0x234,
+       0x268,
+       0x2a2,
+       0x2d6,
+       0x310,
+       0x344,
+       0x378,
+       0x3b2,
+       0x3e6,
+       0x41a,
+       0x454,
+       0x534,
+};
+
+/* ------------------------------------------------------------------------- *
+ *
+ * Backlight operations
+ *
+ * ------------------------------------------------------------------------- */
+
+static int nvidia_set_backlight_enable(int on, int level, void *data)
+{
+       struct nvidia_par *par = (struct nvidia_par *)data;
+       u32 tmp_pcrt, tmp_pmc, fpcontrol;
+
+       tmp_pmc = NV_RD32(par->PMC, 0x10F0) & 0x0000FFFF;
+       tmp_pcrt = NV_RD32(par->PCRTC0, 0x081C) & 0xFFFFFFFC;
+       fpcontrol = NV_RD32(par->PRAMDAC, 0x0848) & 0xCFFFFFCC;
+
+       if (on && (level > BACKLIGHT_OFF)) {
+               tmp_pcrt |= 0x1;
+               tmp_pmc |= (1 << 31);   // backlight bit
+               tmp_pmc |= nvidia_backlight_levels[level - 1] << 16;
+       }
+
+       if (on)
+               fpcontrol |= par->fpSyncs;
+       else
+               fpcontrol |= 0x20000022;
+
+       NV_WR32(par->PCRTC0, 0x081C, tmp_pcrt);
+       NV_WR32(par->PMC, 0x10F0, tmp_pmc);
+       NV_WR32(par->PRAMDAC, 0x848, fpcontrol);
+
+       return 0;
+}
+
+static int nvidia_set_backlight_level(int level, void *data)
+{
+       return nvidia_set_backlight_enable(1, level, data);
+}
+
+static struct backlight_controller nvidia_backlight_controller = {
+       nvidia_set_backlight_enable,
+       nvidia_set_backlight_level
+};
+
+#endif                         /* CONFIG_PMAC_BACKLIGHT */
+
+static void nvidiafb_load_cursor_image(struct nvidia_par *par, u8 * data8,
+                                      u16 bg, u16 fg, u32 w, u32 h)
+{
+       int i, j, k = 0;
+       u32 b, tmp;
+       u32 *data = (u32 *) data8;
+
+       w = (w + 1) & ~1;
+
+       for (i = 0; i < h; i++) {
+               b = *data++;
+               reverse_order(&b);
+
+               for (j = 0; j < w / 2; j++) {
+                       tmp = 0;
+#if defined (__BIG_ENDIAN)
+                       tmp = (b & (1 << 31)) ? fg << 16 : bg << 16;
+                       b <<= 1;
+                       tmp |= (b & (1 << 31)) ? fg : bg;
+                       b <<= 1;
+#else
+                       tmp = (b & 1) ? fg : bg;
+                       b >>= 1;
+                       tmp |= (b & 1) ? fg << 16 : bg << 16;
+                       b >>= 1;
+#endif
+                       NV_WR32(&par->CURSOR[k++], 0, tmp);
+               }
+               k += (MAX_CURS - w) / 2;
+       }
+}
+
+static void nvidia_write_clut(struct nvidia_par *par,
+                             u8 regnum, u8 red, u8 green, u8 blue)
+{
+       NVWriteDacMask(par, 0xff);
+       NVWriteDacWriteAddr(par, regnum);
+       NVWriteDacData(par, red);
+       NVWriteDacData(par, green);
+       NVWriteDacData(par, blue);
+}
+
+static void nvidia_read_clut(struct nvidia_par *par,
+                            u8 regnum, u8 * red, u8 * green, u8 * blue)
+{
+       NVWriteDacMask(par, 0xff);
+       NVWriteDacReadAddr(par, regnum);
+       *red = NVReadDacData(par);
+       *green = NVReadDacData(par);
+       *blue = NVReadDacData(par);
+}
+
+static int nvidia_panel_tweak(struct nvidia_par *par,
+                             struct _riva_hw_state *state)
+{
+       int tweak = 0;
+
+   if (par->paneltweak) {
+          tweak = par->paneltweak;
+   } else {
+          /* begin flat panel hacks */
+          /* This is unfortunate, but some chips need this register
+             tweaked or else you get artifacts where adjacent pixels are
+             swapped.  There are no hard rules for what to set here so all
+             we can do is experiment and apply hacks. */
+
+          if(((par->Chipset & 0xffff) == 0x0328) && (state->bpp == 32)) {
+                  /* At least one NV34 laptop needs this workaround. */
+                  tweak = -1;
+          }
+
+          if((par->Chipset & 0xfff0) == 0x0310) {
+                  tweak = 1;
+          }
+          /* end flat panel hacks */
+   }
+
+   return tweak;
+}
+
+static void nvidia_save_vga(struct nvidia_par *par,
+                           struct _riva_hw_state *state)
+{
+       int i;
+
+       NVTRACE_ENTER();
+       NVLockUnlock(par, 0);
+
+       NVUnloadStateExt(par, state);
+
+       state->misc_output = NVReadMiscOut(par);
+
+       for (i = 0; i < NUM_CRT_REGS; i++)
+               state->crtc[i] = NVReadCrtc(par, i);
+
+       for (i = 0; i < NUM_ATC_REGS; i++)
+               state->attr[i] = NVReadAttr(par, i);
+
+       for (i = 0; i < NUM_GRC_REGS; i++)
+               state->gra[i] = NVReadGr(par, i);
+
+       for (i = 0; i < NUM_SEQ_REGS; i++)
+               state->seq[i] = NVReadSeq(par, i);
+       NVTRACE_LEAVE();
+}
+
+static void nvidia_write_regs(struct nvidia_par *par)
+{
+       struct _riva_hw_state *state = &par->ModeReg;
+       int i;
+
+       NVTRACE_ENTER();
+       NVWriteCrtc(par, 0x11, 0x00);
+
+       NVLockUnlock(par, 0);
+
+       NVLoadStateExt(par, state);
+
+       NVWriteMiscOut(par, state->misc_output);
+
+       for (i = 0; i < NUM_CRT_REGS; i++) {
+               switch (i) {
+               case 0x19:
+               case 0x20 ... 0x40:
+                       break;
+               default:
+                       NVWriteCrtc(par, i, state->crtc[i]);
+               }
+       }
+
+       for (i = 0; i < NUM_ATC_REGS; i++)
+               NVWriteAttr(par, i, state->attr[i]);
+
+       for (i = 0; i < NUM_GRC_REGS; i++)
+               NVWriteGr(par, i, state->gra[i]);
+
+       for (i = 0; i < NUM_SEQ_REGS; i++)
+               NVWriteSeq(par, i, state->seq[i]);
+       NVTRACE_LEAVE();
+}
+
+static int nvidia_calc_regs(struct fb_info *info)
+{
+       struct nvidia_par *par = info->par;
+       struct _riva_hw_state *state = &par->ModeReg;
+       int i, depth = fb_get_color_depth(&info->var);
+       int h_display = info->var.xres / 8 - 1;
+       int h_start = (info->var.xres + info->var.right_margin) / 8 - 1;
+       int h_end = (info->var.xres + info->var.right_margin +
+                    info->var.hsync_len) / 8 - 1;
+       int h_total = (info->var.xres + info->var.right_margin +
+                      info->var.hsync_len + info->var.left_margin) / 8 - 5;
+       int h_blank_s = h_display;
+       int h_blank_e = h_total + 4;
+       int v_display = info->var.yres - 1;
+       int v_start = info->var.yres + info->var.lower_margin - 1;
+       int v_end = (info->var.yres + info->var.lower_margin +
+                    info->var.vsync_len) - 1;
+       int v_total = (info->var.yres + info->var.lower_margin +
+                      info->var.vsync_len + info->var.upper_margin) - 2;
+       int v_blank_s = v_display;
+       int v_blank_e = v_total + 1;
+
+       /*
+        * Set all CRTC values.
+        */
+
+       if (info->var.vmode & FB_VMODE_INTERLACED)
+               v_total |= 1;
+
+       if (par->FlatPanel == 1) {
+               v_start = v_total - 3;
+               v_end = v_total - 2;
+               v_blank_s = v_start;
+               h_start = h_total - 5;
+               h_end = h_total - 2;
+               h_blank_e = h_total + 4;
+       }
+
+       state->crtc[0x0] = Set8Bits(h_total);
+       state->crtc[0x1] = Set8Bits(h_display);
+       state->crtc[0x2] = Set8Bits(h_blank_s);
+       state->crtc[0x3] = SetBitField(h_blank_e, 4: 0, 4:0)
+               | SetBit(7);
+       state->crtc[0x4] = Set8Bits(h_start);
+       state->crtc[0x5] = SetBitField(h_blank_e, 5: 5, 7:7)
+               | SetBitField(h_end, 4: 0, 4:0);
+       state->crtc[0x6] = SetBitField(v_total, 7: 0, 7:0);
+       state->crtc[0x7] = SetBitField(v_total, 8: 8, 0:0)
+               | SetBitField(v_display, 8: 8, 1:1)
+               | SetBitField(v_start, 8: 8, 2:2)
+               | SetBitField(v_blank_s, 8: 8, 3:3)
+               | SetBit(4)
+               | SetBitField(v_total, 9: 9, 5:5)
+               | SetBitField(v_display, 9: 9, 6:6)
+               | SetBitField(v_start, 9: 9, 7:7);
+       state->crtc[0x9] = SetBitField(v_blank_s, 9: 9, 5:5)
+               | SetBit(6)
+               | ((info->var.vmode & FB_VMODE_DOUBLE) ? 0x80 : 0x00);
+       state->crtc[0x10] = Set8Bits(v_start);
+       state->crtc[0x11] = SetBitField(v_end, 3: 0, 3:0) | SetBit(5);
+       state->crtc[0x12] = Set8Bits(v_display);
+       state->crtc[0x13] = ((info->var.xres_virtual / 8) *
+                            (info->var.bits_per_pixel / 8));
+       state->crtc[0x15] = Set8Bits(v_blank_s);
+       state->crtc[0x16] = Set8Bits(v_blank_e);
+
+       state->attr[0x10] = 0x01;
+
+       if (par->Television)
+               state->attr[0x11] = 0x00;
+
+       state->screen = SetBitField(h_blank_e, 6: 6, 4:4)
+               | SetBitField(v_blank_s, 10: 10, 3:3)
+               | SetBitField(v_start, 10: 10, 2:2)
+               | SetBitField(v_display, 10: 10, 1:1)
+               | SetBitField(v_total, 10: 10, 0:0);
+
+       state->horiz = SetBitField(h_total, 8: 8, 0:0)
+               | SetBitField(h_display, 8: 8, 1:1)
+               | SetBitField(h_blank_s, 8: 8, 2:2)
+               | SetBitField(h_start, 8: 8, 3:3);
+
+       state->extra = SetBitField(v_total, 11: 11, 0:0)
+               | SetBitField(v_display, 11: 11, 2:2)
+               | SetBitField(v_start, 11: 11, 4:4)
+               | SetBitField(v_blank_s, 11: 11, 6:6);
+
+       if (info->var.vmode & FB_VMODE_INTERLACED) {
+               h_total = (h_total >> 1) & ~1;
+               state->interlace = Set8Bits(h_total);
+               state->horiz |= SetBitField(h_total, 8: 8, 4:4);
+       } else {
+               state->interlace = 0xff;        /* interlace off */
+       }
+
+       /*
+        * Calculate the extended registers.
+        */
+
+       if (depth < 24)
+               i = depth;
+       else
+               i = 32;
+
+       if (par->Architecture >= NV_ARCH_10)
+               par->CURSOR = (volatile u32 __iomem *)(info->screen_base +
+                                                      par->CursorStart);
+
+       if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
+               state->misc_output &= ~0x40;
+       else
+               state->misc_output |= 0x40;
+       if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
+               state->misc_output &= ~0x80;
+       else
+               state->misc_output |= 0x80;
+
+       NVCalcStateExt(par, state, i, info->var.xres_virtual,
+                      info->var.xres, info->var.yres_virtual,
+                      1000000000 / info->var.pixclock, info->var.vmode);
+
+       state->scale = NV_RD32(par->PRAMDAC, 0x00000848) & 0xfff000ff;
+       if (par->FlatPanel == 1) {
+               state->pixel |= (1 << 7);
+
+               if (!par->fpScaler || (par->fpWidth <= info->var.xres)
+                   || (par->fpHeight <= info->var.yres)) {
+                       state->scale |= (1 << 8);
+               }
+
+               if (!par->crtcSync_read) {
+                       state->crtcSync = NV_RD32(par->PRAMDAC, 0x0828);
+                       par->crtcSync_read = 1;
+               }
+
+               par->PanelTweak = nvidia_panel_tweak(par, state);
+       }
+
+       state->vpll = state->pll;
+       state->vpll2 = state->pll;
+       state->vpllB = state->pllB;
+       state->vpll2B = state->pllB;
+
+       VGA_WR08(par->PCIO, 0x03D4, 0x1C);
+       state->fifo = VGA_RD08(par->PCIO, 0x03D5) & ~(1<<5);
+
+       if (par->CRTCnumber) {
+               state->head = NV_RD32(par->PCRTC0, 0x00000860) & ~0x00001000;
+               state->head2 = NV_RD32(par->PCRTC0, 0x00002860) | 0x00001000;
+               state->crtcOwner = 3;
+               state->pllsel |= 0x20000800;
+               state->vpll = NV_RD32(par->PRAMDAC0, 0x00000508);
+               if (par->twoStagePLL)
+                       state->vpllB = NV_RD32(par->PRAMDAC0, 0x00000578);
+       } else if (par->twoHeads) {
+               state->head = NV_RD32(par->PCRTC0, 0x00000860) | 0x00001000;
+               state->head2 = NV_RD32(par->PCRTC0, 0x00002860) & ~0x00001000;
+               state->crtcOwner = 0;
+               state->vpll2 = NV_RD32(par->PRAMDAC0, 0x0520);
+               if (par->twoStagePLL)
+                       state->vpll2B = NV_RD32(par->PRAMDAC0, 0x057C);
+       }
+
+       state->cursorConfig = 0x00000100;
+
+       if (info->var.vmode & FB_VMODE_DOUBLE)
+               state->cursorConfig |= (1 << 4);
+
+       if (par->alphaCursor) {
+               if ((par->Chipset & 0x0ff0) != 0x0110)
+                       state->cursorConfig |= 0x04011000;
+               else
+                       state->cursorConfig |= 0x14011000;
+               state->general |= (1 << 29);
+       } else
+               state->cursorConfig |= 0x02000000;
+
+       if (par->twoHeads) {
+               if ((par->Chipset & 0x0ff0) == 0x0110) {
+                       state->dither = NV_RD32(par->PRAMDAC, 0x0528) &
+                           ~0x00010000;
+                       if (par->FPDither)
+                               state->dither |= 0x00010000;
+               } else {
+                       state->dither = NV_RD32(par->PRAMDAC, 0x083C) & ~1;
+                       if (par->FPDither)
+                               state->dither |= 1;
+               }
+       }
+
+       state->timingH = 0;
+       state->timingV = 0;
+       state->displayV = info->var.xres;
+
+       return 0;
+}
+
+static void nvidia_init_vga(struct fb_info *info)
+{
+       struct nvidia_par *par = info->par;
+       struct _riva_hw_state *state = &par->ModeReg;
+       int i;
+
+       for (i = 0; i < 0x10; i++)
+               state->attr[i] = i;
+       state->attr[0x10] = 0x41;
+       state->attr[0x11] = 0x01;
+       state->attr[0x12] = 0x0f;
+       state->attr[0x13] = 0x00;
+       state->attr[0x14] = 0x00;
+
+       memset(state->crtc, 0x00, NUM_CRT_REGS);
+       state->crtc[0x0a] = 0x20;
+       state->crtc[0x17] = 0xe3;
+       state->crtc[0x18] = 0xff;
+       state->crtc[0x28] = 0x40;
+
+       memset(state->gra, 0x00, NUM_GRC_REGS);
+       state->gra[0x05] = 0x40;
+       state->gra[0x06] = 0x05;
+       state->gra[0x07] = 0x0f;
+       state->gra[0x08] = 0xff;
+
+       state->seq[0x00] = 0x03;
+       state->seq[0x01] = 0x01;
+       state->seq[0x02] = 0x0f;
+       state->seq[0x03] = 0x00;
+       state->seq[0x04] = 0x0e;
+
+       state->misc_output = 0xeb;
+}
+
+static int nvidiafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+       struct nvidia_par *par = info->par;
+       u8 data[MAX_CURS * MAX_CURS / 8];
+       u16 fg, bg;
+       int i, set = cursor->set;
+
+       if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS)
+               return soft_cursor(info, cursor);
+
+       NVShowHideCursor(par, 0);
+
+       if (par->cursor_reset) {
+               set = FB_CUR_SETALL;
+               par->cursor_reset = 0;
+       }
+
+       if (set & FB_CUR_SETSIZE)
+               memset_io(par->CURSOR, 0, MAX_CURS * MAX_CURS * 2);
+
+       if (set & FB_CUR_SETPOS) {
+               u32 xx, yy, temp;
+
+               yy = cursor->image.dy - info->var.yoffset;
+               xx = cursor->image.dx - info->var.xoffset;
+               temp = xx & 0xFFFF;
+               temp |= yy << 16;
+
+               NV_WR32(par->PRAMDAC, 0x0000300, temp);
+       }
+
+       if (set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) {
+               u32 bg_idx = cursor->image.bg_color;
+               u32 fg_idx = cursor->image.fg_color;
+               u32 s_pitch = (cursor->image.width + 7) >> 3;
+               u32 d_pitch = MAX_CURS / 8;
+               u8 *dat = (u8 *) cursor->image.data;
+               u8 *msk = (u8 *) cursor->mask;
+               u8 *src;
+
+               src = kmalloc(s_pitch * cursor->image.height, GFP_ATOMIC);
+
+               if (src) {
+                       switch (cursor->rop) {
+                       case ROP_XOR:
+                               for (i = 0; i < s_pitch * cursor->image.height;
+                                    i++)
+                                       src[i] = dat[i] ^ msk[i];
+                               break;
+                       case ROP_COPY:
+                       default:
+                               for (i = 0; i < s_pitch * cursor->image.height;
+                                    i++)
+                                       src[i] = dat[i] & msk[i];
+                               break;
+                       }
+
+                       fb_sysmove_buf_aligned(info, &info->pixmap, data,
+                                              d_pitch, src, s_pitch,
+                                              cursor->image.height);
+
+                       bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) |
+                           ((info->cmap.green[bg_idx] & 0xf8) << 2) |
+                           ((info->cmap.blue[bg_idx] & 0xf8) >> 3) | 1 << 15;
+
+                       fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) |
+                           ((info->cmap.green[fg_idx] & 0xf8) << 2) |
+                           ((info->cmap.blue[fg_idx] & 0xf8) >> 3) | 1 << 15;
+
+                       NVLockUnlock(par, 0);
+
+                       nvidiafb_load_cursor_image(par, data, bg, fg,
+                                                  cursor->image.width,
+                                                  cursor->image.height);
+                       kfree(src);
+               }
+       }
+
+       if (cursor->enable)
+               NVShowHideCursor(par, 1);
+
+       return 0;
+}
+
+static int nvidiafb_set_par(struct fb_info *info)
+{
+       struct nvidia_par *par = info->par;
+
+       NVTRACE_ENTER();
+
+       NVLockUnlock(par, 1);
+       if (!par->FlatPanel || (info->var.bits_per_pixel != 24) ||
+           !par->twoHeads)
+               par->FPDither = 0;
+
+       nvidia_init_vga(info);
+       nvidia_calc_regs(info);
+       nvidia_write_regs(par);
+
+       NVLockUnlock(par, 0);
+       if (par->twoHeads) {
+               VGA_WR08(par->PCIO, 0x03D4, 0x44);
+               VGA_WR08(par->PCIO, 0x03D5, par->ModeReg.crtcOwner);
+               NVLockUnlock(par, 0);
+       }
+
+       NVWriteCrtc(par, 0x11, 0x00);
+       info->fix.line_length = (info->var.xres_virtual *
+                                info->var.bits_per_pixel) >> 3;
+       info->fix.visual = (info->var.bits_per_pixel == 8) ?
+           FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
+
+       if (info->var.accel_flags) {
+               info->fbops->fb_imageblit = nvidiafb_imageblit;
+               info->fbops->fb_fillrect = nvidiafb_fillrect;
+               info->fbops->fb_copyarea = nvidiafb_copyarea;
+               info->fbops->fb_sync = nvidiafb_sync;
+               info->pixmap.scan_align = 4;
+               info->flags &= ~FBINFO_HWACCEL_DISABLED;
+               NVResetGraphics(info);
+       } else {
+               info->fbops->fb_imageblit = cfb_imageblit;
+               info->fbops->fb_fillrect = cfb_fillrect;
+               info->fbops->fb_copyarea = cfb_copyarea;
+               info->fbops->fb_sync = NULL;
+               info->pixmap.scan_align = 1;
+               info->flags |= FBINFO_HWACCEL_DISABLED;
+       }
+
+       par->cursor_reset = 1;
+
+       NVWriteCrtc(par, 0x11, 0xff);
+
+       NVTRACE_LEAVE();
+       return 0;
+}
+
+static int nvidiafb_setcolreg(unsigned regno, unsigned red, unsigned green,
+                             unsigned blue, unsigned transp,
+                             struct fb_info *info)
+{
+       struct nvidia_par *par = info->par;
+       int i;
+
+       NVTRACE_ENTER();
+       if (regno >= (1 << info->var.green.length))
+               return -EINVAL;
+
+       if (info->var.grayscale) {
+               /* gray = 0.30*R + 0.59*G + 0.11*B */
+               red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
+       }
+
+       if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+               ((u32 *) info->pseudo_palette)[regno] =
+                   (regno << info->var.red.offset) |
+                   (regno << info->var.green.offset) |
+                   (regno << info->var.blue.offset);
+       }
+
+       switch (info->var.bits_per_pixel) {
+       case 8:
+               /* "transparent" stuff is completely ignored. */
+               nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
+               break;
+       case 16:
+               if (info->var.green.length == 5) {
+                       for (i = 0; i < 8; i++) {
+                               nvidia_write_clut(par, regno * 8 + i, red >> 8,
+                                                 green >> 8, blue >> 8);
+                       }
+               } else {
+                       u8 r, g, b;
+
+                       if (regno < 32) {
+                               for (i = 0; i < 8; i++) {
+                                       nvidia_write_clut(par, regno * 8 + i,
+                                                         red >> 8, green >> 8,
+                                                         blue >> 8);
+                               }
+                       }
+
+                       nvidia_read_clut(par, regno * 4, &r, &g, &b);
+
+                       for (i = 0; i < 4; i++)
+                               nvidia_write_clut(par, regno * 4 + i, r,
+                                                 green >> 8, b);
+               }
+               break;
+       case 32:
+               nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
+               break;
+       default:
+               /* do nothing */
+               break;
+       }
+
+       NVTRACE_LEAVE();
+       return 0;
+}
+
+static int nvidiafb_check_var(struct fb_var_screeninfo *var,
+                             struct fb_info *info)
+{
+       struct nvidia_par *par = info->par;
+       int memlen, vramlen, mode_valid = 0;
+       int pitch, err = 0;
+
+       NVTRACE_ENTER();
+
+       var->transp.offset = 0;
+       var->transp.length = 0;
+
+       var->xres &= ~7;
+
+       if (var->bits_per_pixel <= 8)
+               var->bits_per_pixel = 8;
+       else if (var->bits_per_pixel <= 16)
+               var->bits_per_pixel = 16;
+       else
+               var->bits_per_pixel = 32;
+
+       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:
+               var->green.length = (var->green.length < 6) ? 5 : 6;
+               var->red.length = 5;
+               var->blue.length = 5;
+               var->transp.length = 6 - var->green.length;
+               var->blue.offset = 0;
+               var->green.offset = 5;
+               var->red.offset = 5 + var->green.length;
+               var->transp.offset = (5 + var->red.offset) & 15;
+               break;
+       case 32:                /* RGBA 8888 */
+               var->red.offset = 16;
+               var->red.length = 8;
+               var->green.offset = 8;
+               var->green.length = 8;
+               var->blue.offset = 0;
+               var->blue.length = 8;
+               var->transp.length = 8;
+               var->transp.offset = 24;
+               break;
+       }
+
+       var->red.msb_right = 0;
+       var->green.msb_right = 0;
+       var->blue.msb_right = 0;
+       var->transp.msb_right = 0;
+
+       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) {
+                       fb_videomode_to_var(var, mode);
+                       mode_valid = 1;
+               }
+       }
+
+       if (!mode_valid && info->monspecs.modedb_len)
+               return -EINVAL;
+
+       if (par->fpWidth && par->fpHeight && (par->fpWidth < var->xres ||
+                                             par->fpHeight < var->yres))
+               return -EINVAL;
+
+       if (var->yres_virtual < var->yres)
+               var->yres_virtual = var->yres;
+
+       if (var->xres_virtual < var->xres)
+               var->xres_virtual = var->xres;
+
+       var->xres_virtual = (var->xres_virtual + 63) & ~63;
+
+       vramlen = info->screen_size;
+       pitch = ((var->xres_virtual * var->bits_per_pixel) + 7) / 8;
+       memlen = pitch * var->yres_virtual;
+
+       if (memlen > vramlen) {
+               var->yres_virtual = vramlen / pitch;
+
+               if (var->yres_virtual < var->yres) {
+                       var->yres_virtual = var->yres;
+                       var->xres_virtual = vramlen / var->yres_virtual;
+                       var->xres_virtual /= var->bits_per_pixel / 8;
+                       var->xres_virtual &= ~63;
+                       pitch = (var->xres_virtual *
+                                var->bits_per_pixel + 7) / 8;
+                       memlen = pitch * var->yres;
+
+                       if (var->xres_virtual < var->xres) {
+                               printk("nvidiafb: required video memory, "
+                                      "%d bytes, for %dx%d-%d (virtual) "
+                                      "is out of range\n",
+                                      memlen, var->xres_virtual,
+                                      var->yres_virtual, var->bits_per_pixel);
+                               err = -ENOMEM;
+                       }
+               }
+       }
+
+       if (var->accel_flags) {
+               if (var->yres_virtual > 0x7fff)
+                       var->yres_virtual = 0x7fff;
+               if (var->xres_virtual > 0x7fff)
+                       var->xres_virtual = 0x7fff;
+       }
+
+       var->xres_virtual &= ~63;
+
+       NVTRACE_LEAVE();
+
+       return err;
+}
+
+static int nvidiafb_pan_display(struct fb_var_screeninfo *var,
+                               struct fb_info *info)
+{
+       struct nvidia_par *par = info->par;
+       u32 total;
+
+       total = info->var.yoffset * info->fix.line_length + info->var.xoffset;
+
+       NVSetStartAddress(par, total);
+
+       return 0;
+}
+
+static int nvidiafb_blank(int blank, struct fb_info *info)
+{
+       struct nvidia_par *par = info->par;
+       unsigned char tmp, vesa;
+
+       tmp = NVReadSeq(par, 0x01) & ~0x20;     /* screen on/off */
+       vesa = NVReadCrtc(par, 0x1a) & ~0xc0;   /* sync on/off */
+
+       NVTRACE_ENTER();
+
+       if (blank)
+               tmp |= 0x20;
+
+       switch (blank) {
+       case FB_BLANK_UNBLANK:
+       case FB_BLANK_NORMAL:
+               break;
+       case FB_BLANK_VSYNC_SUSPEND:
+               vesa |= 0x80;
+               break;
+       case FB_BLANK_HSYNC_SUSPEND:
+               vesa |= 0x40;
+               break;
+       case FB_BLANK_POWERDOWN:
+               vesa |= 0xc0;
+               break;
+       }
+
+       NVWriteSeq(par, 0x01, tmp);
+       NVWriteCrtc(par, 0x1a, vesa);
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+       if (par->FlatPanel && _machine == _MACH_Pmac) {
+               set_backlight_enable(!blank);
+       }
+#endif
+
+       NVTRACE_LEAVE();
+
+       return 0;
+}
+
+static struct fb_ops nvidia_fb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_check_var   = nvidiafb_check_var,
+       .fb_set_par     = nvidiafb_set_par,
+       .fb_setcolreg   = nvidiafb_setcolreg,
+       .fb_pan_display = nvidiafb_pan_display,
+       .fb_blank       = nvidiafb_blank,
+       .fb_fillrect    = nvidiafb_fillrect,
+       .fb_copyarea    = nvidiafb_copyarea,
+       .fb_imageblit   = nvidiafb_imageblit,
+       .fb_cursor      = nvidiafb_cursor,
+       .fb_sync        = nvidiafb_sync,
+};
+
+static int __devinit nvidia_set_fbinfo(struct fb_info *info)
+{
+       struct fb_monspecs *specs = &info->monspecs;
+       struct fb_videomode modedb;
+       struct nvidia_par *par = info->par;
+       int lpitch;
+
+       NVTRACE_ENTER();
+       info->flags = FBINFO_DEFAULT
+           | FBINFO_HWACCEL_IMAGEBLIT
+           | FBINFO_HWACCEL_FILLRECT
+           | FBINFO_HWACCEL_COPYAREA
+           | FBINFO_HWACCEL_YPAN;
+
+       fb_videomode_to_modelist(info->monspecs.modedb,
+                                info->monspecs.modedb_len, &info->modelist);
+       fb_var_to_videomode(&modedb, &nvidiafb_default_var);
+
+       if (specs->modedb != NULL) {
+               /* get preferred timing */
+               if (specs->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];
+               }
+
+               fb_videomode_to_var(&nvidiafb_default_var, &modedb);
+               nvidiafb_default_var.bits_per_pixel = 8;
+       }
+
+       if (mode_option)
+               fb_find_mode(&nvidiafb_default_var, info, mode_option,
+                            specs->modedb, specs->modedb_len, &modedb, 8);
+
+       info->var = nvidiafb_default_var;
+       info->fix.visual = (info->var.bits_per_pixel == 8) ?
+               FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
+       info->pseudo_palette = par->pseudo_palette;
+       fb_alloc_cmap(&info->cmap, 256, 0);
+       fb_destroy_modedb(info->monspecs.modedb);
+       info->monspecs.modedb = NULL;
+
+       /* maximize virtual vertical length */
+       lpitch = info->var.xres_virtual *
+               ((info->var.bits_per_pixel + 7) >> 3);
+       info->var.yres_virtual = info->screen_size / lpitch;
+
+       info->pixmap.scan_align = 4;
+       info->pixmap.buf_align = 4;
+       info->pixmap.size = 8 * 1024;
+       info->pixmap.flags = FB_PIXMAP_SYSTEM;
+
+       if (!hwcur)
+               info->fbops->fb_cursor = soft_cursor;
+       info->var.accel_flags = (!noaccel);
+
+       switch (par->Architecture) {
+       case NV_ARCH_04:
+               info->fix.accel = FB_ACCEL_NV4;
+               break;
+       case NV_ARCH_10:
+               info->fix.accel = FB_ACCEL_NV_10;
+               break;
+       case NV_ARCH_20:
+               info->fix.accel = FB_ACCEL_NV_20;
+               break;
+       case NV_ARCH_30:
+               info->fix.accel = FB_ACCEL_NV_30;
+               break;
+       case NV_ARCH_40:
+               info->fix.accel = FB_ACCEL_NV_40;
+               break;
+       }
+
+       NVTRACE_LEAVE();
+
+       return nvidiafb_check_var(&info->var, info);
+}
+
+static u32 __devinit nvidia_get_arch(struct pci_dev *pd)
+{
+       u32 arch = 0;
+
+       switch (pd->device & 0x0ff0) {
+       case 0x0100:            /* GeForce 256 */
+       case 0x0110:            /* GeForce2 MX */
+       case 0x0150:            /* GeForce2 */
+       case 0x0170:            /* GeForce4 MX */
+       case 0x0180:            /* GeForce4 MX (8x AGP) */
+       case 0x01A0:            /* nForce */
+       case 0x01F0:            /* nForce2 */
+               arch = NV_ARCH_10;
+               break;
+       case 0x0200:            /* GeForce3 */
+       case 0x0250:            /* GeForce4 Ti */
+       case 0x0280:            /* GeForce4 Ti (8x AGP) */
+               arch = NV_ARCH_20;
+               break;
+       case 0x0300:            /* GeForceFX 5800 */
+       case 0x0310:            /* GeForceFX 5600 */
+       case 0x0320:            /* GeForceFX 5200 */
+       case 0x0330:            /* GeForceFX 5900 */
+       case 0x0340:            /* GeForceFX 5700 */
+               arch = NV_ARCH_30;
+               break;
+       case 0x0040:
+       case 0x00C0:
+       case 0x0120:
+       case 0x0130:
+       case 0x0140:
+       case 0x0160:
+       case 0x01D0:
+       case 0x0090:
+       case 0x0210:
+       case 0x0220:
+       case 0x0230:
+               arch = NV_ARCH_40;
+               break;
+       case 0x0020:            /* TNT, TNT2 */
+               arch = NV_ARCH_04;
+               break;
+       default:                /* unknown architecture */
+               break;
+       }
+
+       return arch;
+}
+
+static int __devinit nvidiafb_probe(struct pci_dev *pd,
+                                   const struct pci_device_id *ent)
+{
+       struct nvidia_par *par;
+       struct fb_info *info;
+       unsigned short cmd;
+
+
+       NVTRACE_ENTER();
+       assert(pd != NULL);
+
+       info = framebuffer_alloc(sizeof(struct nvidia_par), &pd->dev);
+
+       if (!info)
+               goto err_out;
+
+       par = (struct nvidia_par *)info->par;
+       par->pci_dev = pd;
+
+       info->pixmap.addr = kmalloc(8 * 1024, GFP_KERNEL);
+
+       if (info->pixmap.addr == NULL)
+               goto err_out_kfree;
+
+       memset(info->pixmap.addr, 0, 8 * 1024);
+
+       if (pci_enable_device(pd)) {
+               printk(KERN_ERR PFX "cannot enable PCI device\n");
+               goto err_out_enable;
+       }
+
+       if (pci_request_regions(pd, "nvidiafb")) {
+               printk(KERN_ERR PFX "cannot request PCI regions\n");
+               goto err_out_request;
+       }
+
+       par->Architecture = nvidia_get_arch(pd);
+
+       par->Chipset = (pd->vendor << 16) | pd->device;
+       printk(KERN_INFO PFX "nVidia device/chipset %X\n", par->Chipset);
+
+#ifdef CONFIG_PCI_NAMES
+       printk(KERN_INFO PFX "%s\n", pd->pretty_name);
+#endif
+
+       if (par->Architecture == 0) {
+               printk(KERN_ERR PFX "unknown NV_ARCH\n");
+               goto err_out_free_base0;
+       }
+
+       sprintf(nvidiafb_fix.id, "NV%x", (pd->device & 0x0ff0) >> 4);
+
+       par->FlatPanel = flatpanel;
+
+       if (flatpanel == 1)
+               printk(KERN_INFO PFX "flatpanel support enabled\n");
+
+       par->CRTCnumber = forceCRTC;
+       par->FpScale = (!noscale);
+       par->paneltweak = paneltweak;
+
+       /* enable IO and mem if not already done */
+       pci_read_config_word(pd, PCI_COMMAND, &cmd);
+       cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+       pci_write_config_word(pd, PCI_COMMAND, cmd);
+
+       nvidiafb_fix.mmio_start = pci_resource_start(pd, 0);
+       nvidiafb_fix.smem_start = pci_resource_start(pd, 1);
+       nvidiafb_fix.mmio_len = pci_resource_len(pd, 0);
+
+       par->REGS = ioremap(nvidiafb_fix.mmio_start, nvidiafb_fix.mmio_len);
+
+       if (!par->REGS) {
+               printk(KERN_ERR PFX "cannot ioremap MMIO base\n");
+               goto err_out_free_base0;
+       }
+
+       NVCommonSetup(info);
+
+       par->FbAddress = nvidiafb_fix.smem_start;
+       par->FbMapSize = par->RamAmountKBytes * 1024;
+       if (vram && vram * 1024 * 1024 < par->FbMapSize)
+               par->FbMapSize = vram * 1024 * 1024;
+
+       /* Limit amount of vram to 64 MB */
+       if (par->FbMapSize > 64 * 1024 * 1024)
+               par->FbMapSize = 64 * 1024 * 1024;
+
+       par->FbUsableSize = par->FbMapSize - (128 * 1024);
+       par->ScratchBufferSize = (par->Architecture < NV_ARCH_10) ? 8 * 1024 :
+           16 * 1024;
+       par->ScratchBufferStart = par->FbUsableSize - par->ScratchBufferSize;
+       info->screen_base = ioremap(nvidiafb_fix.smem_start, par->FbMapSize);
+       info->screen_size = par->FbUsableSize;
+       nvidiafb_fix.smem_len = par->RamAmountKBytes * 1024;
+
+       if (!info->screen_base) {
+               printk(KERN_ERR PFX "cannot ioremap FB base\n");
+               goto err_out_free_base1;
+       }
+
+       par->FbStart = info->screen_base;
+
+#ifdef CONFIG_MTRR
+       if (!nomtrr) {
+               par->mtrr.vram = mtrr_add(nvidiafb_fix.smem_start,
+                                         par->RamAmountKBytes * 1024,
+                                         MTRR_TYPE_WRCOMB, 1);
+               if (par->mtrr.vram < 0) {
+                       printk(KERN_ERR PFX "unable to setup MTRR\n");
+               } else {
+                       par->mtrr.vram_valid = 1;
+                       /* let there be speed */
+                       printk(KERN_INFO PFX "MTRR set to ON\n");
+               }
+       }
+#endif                         /* CONFIG_MTRR */
+
+       info->fbops = &nvidia_fb_ops;
+       info->fix = nvidiafb_fix;
+
+       if (nvidia_set_fbinfo(info) < 0) {
+               printk(KERN_ERR PFX "error setting initial video mode\n");
+               goto err_out_iounmap_fb;
+       }
+
+       nvidia_save_vga(par, &par->SavedReg);
+
+       if (register_framebuffer(info) < 0) {
+               printk(KERN_ERR PFX "error registering nVidia framebuffer\n");
+               goto err_out_iounmap_fb;
+       }
+
+       pci_set_drvdata(pd, info);
+
+       printk(KERN_INFO PFX
+              "PCI nVidia %s framebuffer (%dMB @ 0x%lX)\n",
+              info->fix.id,
+              par->FbMapSize / (1024 * 1024), info->fix.smem_start);
+#ifdef CONFIG_PMAC_BACKLIGHT
+       if (par->FlatPanel && _machine == _MACH_Pmac)
+               register_backlight_controller(&nvidia_backlight_controller,
+                                             par, "mnca");
+#endif
+       NVTRACE_LEAVE();
+       return 0;
+
+      err_out_iounmap_fb:
+       iounmap(info->screen_base);
+      err_out_free_base1:
+       fb_destroy_modedb(info->monspecs.modedb);
+       nvidia_delete_i2c_busses(par);
+       iounmap(par->REGS);
+      err_out_free_base0:
+       pci_release_regions(pd);
+      err_out_request:
+       pci_disable_device(pd);
+      err_out_enable:
+       kfree(info->pixmap.addr);
+      err_out_kfree:
+       framebuffer_release(info);
+      err_out:
+       return -ENODEV;
+}
+
+static void __exit nvidiafb_remove(struct pci_dev *pd)
+{
+       struct fb_info *info = pci_get_drvdata(pd);
+       struct nvidia_par *par = info->par;
+
+       NVTRACE_ENTER();
+       if (!info)
+               return;
+
+       unregister_framebuffer(info);
+#ifdef CONFIG_MTRR
+       if (par->mtrr.vram_valid)
+               mtrr_del(par->mtrr.vram, info->fix.smem_start,
+                        info->fix.smem_len);
+#endif                         /* CONFIG_MTRR */
+
+       iounmap(info->screen_base);
+       fb_destroy_modedb(info->monspecs.modedb);
+       nvidia_delete_i2c_busses(par);
+       iounmap(par->REGS);
+       pci_release_regions(pd);
+       pci_disable_device(pd);
+       kfree(info->pixmap.addr);
+       framebuffer_release(info);
+       pci_set_drvdata(pd, NULL);
+       NVTRACE_LEAVE();
+}
+
+/* ------------------------------------------------------------------------- *
+ *
+ * initialization
+ *
+ * ------------------------------------------------------------------------- */
+
+#ifndef MODULE
+static int __devinit nvidiafb_setup(char *options)
+{
+       char *this_opt;
+
+       NVTRACE_ENTER();
+       if (!options || !*options)
+               return 0;
+
+       while ((this_opt = strsep(&options, ",")) != NULL) {
+               if (!strncmp(this_opt, "forceCRTC", 9)) {
+                       char *p;
+
+                       p = this_opt + 9;
+                       if (!*p || !*(++p))
+                               continue;
+                       forceCRTC = *p - '0';
+                       if (forceCRTC < 0 || forceCRTC > 1)
+                               forceCRTC = -1;
+               } else if (!strncmp(this_opt, "flatpanel", 9)) {
+                       flatpanel = 1;
+               } else if (!strncmp(this_opt, "hwcur", 5)) {
+                       hwcur = 1;
+               } else if (!strncmp(this_opt, "noaccel", 6)) {
+                       noaccel = 1;
+               } else if (!strncmp(this_opt, "noscale", 7)) {
+                       noscale = 1;
+               } else if (!strncmp(this_opt, "paneltweak:", 11)) {
+                       paneltweak = simple_strtoul(this_opt+11, NULL, 0);
+               } else if (!strncmp(this_opt, "vram:", 5)) {
+                       vram = simple_strtoul(this_opt+5, NULL, 0);
+#ifdef CONFIG_MTRR
+               } else if (!strncmp(this_opt, "nomtrr", 6)) {
+                       nomtrr = 1;
+#endif
+               } else
+                       mode_option = this_opt;
+       }
+       NVTRACE_LEAVE();
+       return 0;
+}
+#endif                         /* !MODULE */
+
+static struct pci_driver nvidiafb_driver = {
+       .name = "nvidiafb",
+       .id_table = nvidiafb_pci_tbl,
+       .probe = nvidiafb_probe,
+       .remove = __exit_p(nvidiafb_remove),
+};
+
+/* ------------------------------------------------------------------------- *
+ *
+ * modularization
+ *
+ * ------------------------------------------------------------------------- */
+
+static int __devinit nvidiafb_init(void)
+{
+#ifndef MODULE
+       char *option = NULL;
+
+       if (fb_get_options("nvidiafb", &option))
+               return -ENODEV;
+       nvidiafb_setup(option);
+#endif
+       return pci_register_driver(&nvidiafb_driver);
+}
+
+module_init(nvidiafb_init);
+
+#ifdef MODULE
+static void __exit nvidiafb_exit(void)
+{
+       pci_unregister_driver(&nvidiafb_driver);
+}
+
+module_exit(nvidiafb_exit);
+
+module_param(flatpanel, int, 0);
+MODULE_PARM_DESC(flatpanel,
+                "Enables experimental flat panel support for some chipsets. "
+                "(0 or 1=enabled) (default=0)");
+module_param(hwcur, int, 0);
+MODULE_PARM_DESC(hwcur,
+                "Enables hardware cursor implementation. (0 or 1=enabled) "
+                "(default=0)");
+module_param(noaccel, int, 0);
+MODULE_PARM_DESC(noaccel,
+                "Disables hardware acceleration. (0 or 1=disable) "
+                "(default=0)");
+module_param(noscale, int, 0);
+MODULE_PARM_DESC(noscale,
+                "Disables screen scaleing. (0 or 1=disable) "
+                "(default=0, do scaling)");
+module_param(paneltweak, int, 0);
+MODULE_PARM_DESC(paneltweak,
+                "Tweak display settings for flatpanels. "
+                "(default=0, no tweaks)");
+module_param(forceCRTC, int, 0);
+MODULE_PARM_DESC(forceCRTC,
+                "Forces usage of a particular CRTC in case autodetection "
+                "fails. (0 or 1) (default=autodetect)");
+module_param(vram, int, 0);
+MODULE_PARM_DESC(vram,
+                "amount of framebuffer memory to remap in MiB"
+                "(default=0 - remap entire memory)");
+#ifdef CONFIG_MTRR
+module_param(nomtrr, bool, 0);
+MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) "
+                "(default=0)");
+#endif
+
+MODULE_AUTHOR("Antonino Daplas");
+MODULE_DESCRIPTION("Framebuffer driver for nVidia graphics chipset");
+MODULE_LICENSE("GPL");
+#endif                         /* MODULE */
+
diff --git a/drivers/video/s1d13xxxfb.c b/drivers/video/s1d13xxxfb.c
new file mode 100644 (file)
index 0000000..b637c38
--- /dev/null
@@ -0,0 +1,772 @@
+/* drivers/video/s1d13xxxfb.c
+ *
+ * (c) 2004 Simtec Electronics
+ * (c) 2005 Thibaut VARENE <varenet@parisc-linux.org>
+ *
+ * Driver for Epson S1D13xxx series framebuffer chips
+ *
+ * Adapted from
+ *  linux/drivers/video/skeletonfb.c
+ *  linux/drivers/video/epson1355fb.c
+ *  linux/drivers/video/epson/s1d13xxxfb.c (2.4 driver by Epson)
+ *
+ * Note, currently only tested on S1D13806 with 16bit CRT.
+ * As such, this driver might still contain some hardcoded bits relating to
+ * S1D13806.
+ * Making it work on other S1D13XXX chips should merely be a matter of adding
+ * a few switch()s, some missing glue here and there maybe, and split header
+ * files.
+ *
+ * TODO: - handle dual screen display (CRT and LCD at the same time).
+ *      - check_var(), mode change, etc.
+ *      - PM untested.
+ *      - Accelerated interfaces.
+ *      - Probably not SMP safe :)
+ *
+ * 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/device.h>
+#include <linux/delay.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/fb.h>
+
+#include <asm/io.h>
+
+#include <video/s1d13xxxfb.h>
+
+#define PFX "s1d13xxxfb: "
+
+#if 0
+#define dbg(fmt, args...) do { printk(KERN_INFO fmt, ## args); } while(0)
+#else
+#define dbg(fmt, args...) do { } while (0)
+#endif
+
+/*
+ * Here we define the default struct fb_fix_screeninfo
+ */
+static struct fb_fix_screeninfo __devinitdata s1d13xxxfb_fix = {
+       .id             = S1D_FBID,
+       .type           = FB_TYPE_PACKED_PIXELS,
+       .visual         = FB_VISUAL_PSEUDOCOLOR,
+       .xpanstep       = 0,
+       .ypanstep       = 1,
+       .ywrapstep      = 0,
+       .accel          = FB_ACCEL_NONE,
+};
+
+static inline u8
+s1d13xxxfb_readreg(struct s1d13xxxfb_par *par, u16 regno)
+{
+       return readb(par->regs + regno);
+}
+
+static inline void
+s1d13xxxfb_writereg(struct s1d13xxxfb_par *par, u16 regno, u8 value)
+{
+       writeb(value, par->regs + regno);
+}
+
+static inline void
+s1d13xxxfb_runinit(struct s1d13xxxfb_par *par,
+                       const struct s1d13xxxfb_regval *initregs,
+                       const unsigned int size)
+{
+       int i;
+
+       for (i = 0; i < size; i++) {
+               if ((initregs[i].addr == S1DREG_DELAYOFF) ||
+                               (initregs[i].addr == S1DREG_DELAYON))
+                       mdelay((int)initregs[i].value);
+               else {
+                       s1d13xxxfb_writereg(par, initregs[i].addr, initregs[i].value);
+               }
+        }
+
+       /* make sure the hardware can cope with us */
+       mdelay(1);
+}
+
+static inline void
+lcd_enable(struct s1d13xxxfb_par *par, int enable)
+{
+       u8 mode = s1d13xxxfb_readreg(par, S1DREG_COM_DISP_MODE);
+
+       if (enable)
+               mode |= 0x01;
+       else
+               mode &= ~0x01;
+
+       s1d13xxxfb_writereg(par, S1DREG_COM_DISP_MODE, mode);
+}
+
+static inline void
+crt_enable(struct s1d13xxxfb_par *par, int enable)
+{
+       u8 mode = s1d13xxxfb_readreg(par, S1DREG_COM_DISP_MODE);
+
+       if (enable)
+               mode |= 0x02;
+       else
+               mode &= ~0x02;
+
+       s1d13xxxfb_writereg(par, S1DREG_COM_DISP_MODE, mode);
+}
+
+/* framebuffer control routines */
+
+static inline void
+s1d13xxxfb_setup_pseudocolour(struct fb_info *info)
+{
+       info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+
+       info->var.red.length = 4;
+       info->var.green.length = 4;
+       info->var.blue.length = 4;
+}
+
+static inline void
+s1d13xxxfb_setup_truecolour(struct fb_info *info)
+{
+       info->fix.visual = FB_VISUAL_TRUECOLOR;
+       info->var.bits_per_pixel = 16;
+
+       info->var.red.length = 5;
+       info->var.red.offset = 11;
+
+       info->var.green.length = 6;
+       info->var.green.offset = 5;
+
+       info->var.blue.length = 5;
+       info->var.blue.offset = 0;
+}
+
+/**
+ *      s1d13xxxfb_set_par - Alters the hardware state.
+ *      @info: frame buffer structure
+ *
+ *     Using the fb_var_screeninfo in fb_info we set the depth of the
+ *     framebuffer. This function alters the par AND the
+ *     fb_fix_screeninfo stored in fb_info. It doesn't not alter var in
+ *     fb_info since we are using that data. This means we depend on the
+ *     data in var inside fb_info to be supported by the hardware.
+ *     xxxfb_check_var is always called before xxxfb_set_par to ensure this.
+ *
+ *     XXX TODO: write proper s1d13xxxfb_check_var(), without which that
+ *     function is quite useless.
+ */
+static int
+s1d13xxxfb_set_par(struct fb_info *info)
+{
+       struct s1d13xxxfb_par *s1dfb = info->par;
+       unsigned int val;
+
+       dbg("s1d13xxxfb_set_par: bpp=%d\n", info->var.bits_per_pixel);
+
+       if ((s1dfb->display & 0x01))    /* LCD */
+               val = s1d13xxxfb_readreg(s1dfb, S1DREG_LCD_DISP_MODE);   /* read colour control */
+       else    /* CRT */
+               val = s1d13xxxfb_readreg(s1dfb, S1DREG_CRT_DISP_MODE);   /* read colour control */
+
+       val &= ~0x07;
+
+       switch (info->var.bits_per_pixel) {
+               case 4:
+                       dbg("pseudo colour 4\n");
+                       s1d13xxxfb_setup_pseudocolour(info);
+                       val |= 2;
+                       break;
+               case 8:
+                       dbg("pseudo colour 8\n");
+                       s1d13xxxfb_setup_pseudocolour(info);
+                       val |= 3;
+                       break;
+               case 16:
+                       dbg("true colour\n");
+                       s1d13xxxfb_setup_truecolour(info);
+                       val |= 5;
+                       break;
+
+               default:
+                       dbg("bpp not supported!\n");
+                       return -EINVAL;
+       }
+
+       dbg("writing %02x to display mode register\n", val);
+
+       if ((s1dfb->display & 0x01))    /* LCD */
+               s1d13xxxfb_writereg(s1dfb, S1DREG_LCD_DISP_MODE, val);
+       else    /* CRT */
+               s1d13xxxfb_writereg(s1dfb, S1DREG_CRT_DISP_MODE, val);
+
+       info->fix.line_length  = info->var.xres * info->var.bits_per_pixel;
+       info->fix.line_length /= 8;
+
+       dbg("setting line_length to %d\n", info->fix.line_length);
+
+       dbg("done setup\n");
+
+       return 0;
+}
+
+/**
+ *     s1d13xxxfb_setcolreg - sets a color register.
+ *      @regno: Which register in the CLUT we are programming
+ *      @red: The red value which can be up to 16 bits wide
+ *     @green: The green value which can be up to 16 bits wide
+ *     @blue:  The blue value which can be up to 16 bits wide.
+ *     @transp: If supported the alpha value which can be up to 16 bits wide.
+ *      @info: frame buffer info structure
+ *
+ *     Returns negative errno on error, or zero on success.
+ */
+static int
+s1d13xxxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+                       u_int transp, struct fb_info *info)
+{
+       struct s1d13xxxfb_par *s1dfb = info->par;
+       unsigned int pseudo_val;
+
+       if (regno >= S1D_PALETTE_SIZE)
+               return -EINVAL;
+
+       dbg("s1d13xxxfb_setcolreg: %d: rgb=%d,%d,%d, tr=%d\n",
+                   regno, red, green, blue, transp);
+
+       if (info->var.grayscale)
+               red = green = blue = (19595*red + 38470*green + 7471*blue) >> 16;
+
+       switch (info->fix.visual) {
+               case FB_VISUAL_TRUECOLOR:
+                       if (regno >= 16)
+                               return -EINVAL;
+
+                       /* deal with creating pseudo-palette entries */
+
+                       pseudo_val  = (red   >> 11) << info->var.red.offset;
+                       pseudo_val |= (green >> 10) << info->var.green.offset;
+                       pseudo_val |= (blue  >> 11) << info->var.blue.offset;
+
+                       dbg("s1d13xxxfb_setcolreg: pseudo %d, val %08x\n",
+                                   regno, pseudo_val);
+
+                       ((u32 *)info->pseudo_palette)[regno] = pseudo_val;
+
+                       break;
+               case FB_VISUAL_PSEUDOCOLOR:
+                       s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_ADDR, regno);
+                       s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_DATA, red);
+                       s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_DATA, green);
+                       s1d13xxxfb_writereg(s1dfb, S1DREG_LKUP_DATA, blue);
+
+                       break;
+               default:
+                       return -ENOSYS;
+       }
+
+       dbg("s1d13xxxfb_setcolreg: done\n");
+
+       return 0;
+}
+
+/**
+ *      s1d13xxxfb_blank - blanks the display.
+ *      @blank_mode: the blank mode we want.
+ *      @info: frame buffer structure that represents a single frame buffer
+ *
+ *      Blank the screen if blank_mode != 0, else unblank. Return 0 if
+ *      blanking succeeded, != 0 if un-/blanking failed due to e.g. a
+ *      video mode which doesn't support it. Implements VESA suspend
+ *      and powerdown modes on hardware that supports disabling hsync/vsync:
+ *      blank_mode == 2: suspend vsync
+ *      blank_mode == 3: suspend hsync
+ *      blank_mode == 4: powerdown
+ *
+ *      Returns negative errno on error, or zero on success.
+ */
+static int
+s1d13xxxfb_blank(int blank_mode, struct fb_info *info)
+{
+       struct s1d13xxxfb_par *par = info->par;
+
+       dbg("s1d13xxxfb_blank: blank=%d, info=%p\n", blank_mode, info);
+
+       switch (blank_mode) {
+               case FB_BLANK_UNBLANK:
+               case FB_BLANK_NORMAL:
+                       if ((par->display & 0x01) != 0)
+                               lcd_enable(par, 1);
+                       if ((par->display & 0x02) != 0)
+                               crt_enable(par, 1);
+                       break;
+               case FB_BLANK_VSYNC_SUSPEND:
+               case FB_BLANK_HSYNC_SUSPEND:
+                       break;
+               case FB_BLANK_POWERDOWN:
+                       lcd_enable(par, 0);
+                       crt_enable(par, 0);
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       /* let fbcon do a soft blank for us */
+       return ((blank_mode == FB_BLANK_NORMAL) ? 1 : 0);
+}
+
+/**
+ *      s1d13xxxfb_pan_display - Pans the display.
+ *      @var: frame buffer variable screen structure
+ *      @info: frame buffer structure that represents a single frame buffer
+ *
+ *     Pan (or wrap, depending on the `vmode' field) the display using the
+ *     `yoffset' field of the `var' structure (`xoffset'  not yet supported).
+ *     If the values don't fit, return -EINVAL.
+ *
+ *      Returns negative errno on error, or zero on success.
+ */
+static int
+s1d13xxxfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       struct s1d13xxxfb_par *par = info->par;
+       u32 start;
+
+       if (var->xoffset != 0)  /* not yet ... */
+               return -EINVAL;
+
+       if (var->yoffset + info->var.yres > info->var.yres_virtual)
+               return -EINVAL;
+
+       start = (info->fix.line_length >> 1) * var->yoffset;
+
+       if ((par->display & 0x01)) {
+               /* LCD */
+               s1d13xxxfb_writereg(par, S1DREG_LCD_DISP_START0, (start & 0xff));
+               s1d13xxxfb_writereg(par, S1DREG_LCD_DISP_START1, ((start >> 8) & 0xff));
+               s1d13xxxfb_writereg(par, S1DREG_LCD_DISP_START2, ((start >> 16) & 0x0f));
+       } else {
+               /* CRT */
+               s1d13xxxfb_writereg(par, S1DREG_CRT_DISP_START0, (start & 0xff));
+               s1d13xxxfb_writereg(par, S1DREG_CRT_DISP_START1, ((start >> 8) & 0xff));
+               s1d13xxxfb_writereg(par, S1DREG_CRT_DISP_START2, ((start >> 16) & 0x0f));
+       }
+
+       return 0;
+}
+
+
+/* framebuffer information structures */
+
+static struct fb_ops s1d13xxxfb_fbops = {
+       .owner          = THIS_MODULE,
+       .fb_set_par     = s1d13xxxfb_set_par,
+       .fb_setcolreg   = s1d13xxxfb_setcolreg,
+       .fb_blank       = s1d13xxxfb_blank,
+
+       .fb_pan_display = s1d13xxxfb_pan_display,
+
+       /* to be replaced by any acceleration we can */
+       .fb_fillrect    = cfb_fillrect,
+       .fb_copyarea    = cfb_copyarea,
+       .fb_imageblit   = cfb_imageblit,
+       .fb_cursor      = soft_cursor
+};
+
+static int s1d13xxxfb_width_tab[2][4] __devinitdata = {
+       {4, 8, 16, -1},
+       {9, 12, 18, -1},
+};
+
+/**
+ *      s1d13xxxfb_fetch_hw_state - Configure the framebuffer according to
+ *     hardware setup.
+ *      @info: frame buffer structure
+ *
+ *     We setup the framebuffer structures according to the current
+ *     hardware setup. On some machines, the BIOS will have filled
+ *     the chip registers with such info, on others, these values will
+ *     have been written in some init procedure. In any case, the
+ *     software values needs to match the hardware ones. This is what
+ *     this function ensures.
+ *
+ *     Note: some of the hardcoded values here might need some love to
+ *     work on various chips, and might need to no longer be hardcoded.
+ */
+static void __devinit
+s1d13xxxfb_fetch_hw_state(struct fb_info *info)
+{
+       struct fb_var_screeninfo *var = &info->var;
+       struct fb_fix_screeninfo *fix = &info->fix;
+       struct s1d13xxxfb_par *par = info->par;
+       u8 panel, display;
+       u16 offset;
+       u32 xres, yres;
+       u32 xres_virtual, yres_virtual;
+       int bpp, lcd_bpp;
+       int is_color, is_dual, is_tft;
+       int lcd_enabled, crt_enabled;
+
+       fix->type = FB_TYPE_PACKED_PIXELS;
+
+       /* general info */
+       par->display = s1d13xxxfb_readreg(par, S1DREG_COM_DISP_MODE);
+       crt_enabled = (par->display & 0x02) != 0;
+       lcd_enabled = (par->display & 0x01) != 0;
+
+       if (lcd_enabled && crt_enabled)
+               printk(KERN_WARNING PFX "Warning: LCD and CRT detected, using LCD\n");
+
+       if (lcd_enabled)
+               display = s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_MODE);
+       else    /* CRT */
+               display = s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_MODE);
+
+       bpp = display & 0x07;
+
+       switch (bpp) {
+               case 2: /* 4 bpp */
+               case 3: /* 8 bpp */
+                       var->bits_per_pixel = 8;
+                       var->red.offset = var->green.offset = var->blue.offset = 0;
+                       var->red.length = var->green.length = var->blue.length = 8;
+                       break;
+               case 5: /* 16 bpp */
+                       s1d13xxxfb_setup_truecolour(info);
+                       break;
+               default:
+                       dbg("bpp: %i\n", bpp);
+       }
+       fb_alloc_cmap(&info->cmap, 256, 0);
+
+       /* LCD info */
+       panel = s1d13xxxfb_readreg(par, S1DREG_PANEL_TYPE);
+       is_color = (panel & 0x04) != 0;
+       is_dual = (panel & 0x02) != 0;
+       is_tft = (panel & 0x01) != 0;
+       lcd_bpp = s1d13xxxfb_width_tab[is_tft][(panel >> 4) & 3];
+
+       if (lcd_enabled) {
+               xres = (s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_HWIDTH) + 1) * 8;
+               yres = (s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_VHEIGHT0) +
+                       ((s1d13xxxfb_readreg(par, S1DREG_LCD_DISP_VHEIGHT1) & 0x03) << 8) + 1);
+
+               offset = (s1d13xxxfb_readreg(par, S1DREG_LCD_MEM_OFF0) +
+                       ((s1d13xxxfb_readreg(par, S1DREG_LCD_MEM_OFF1) & 0x7) << 8));
+       } else { /* crt */
+               xres = (s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_HWIDTH) + 1) * 8;
+               yres = (s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_VHEIGHT0) +
+                       ((s1d13xxxfb_readreg(par, S1DREG_CRT_DISP_VHEIGHT1) & 0x03) << 8) + 1);
+
+               offset = (s1d13xxxfb_readreg(par, S1DREG_CRT_MEM_OFF0) +
+                       ((s1d13xxxfb_readreg(par, S1DREG_CRT_MEM_OFF1) & 0x7) << 8));
+       }
+       xres_virtual = offset * 16 / var->bits_per_pixel;
+       yres_virtual = fix->smem_len / (offset * 2);
+
+       var->xres               = xres;
+       var->yres               = yres;
+       var->xres_virtual       = xres_virtual;
+       var->yres_virtual       = yres_virtual;
+       var->xoffset            = var->yoffset = 0;
+
+       fix->line_length        = offset * 2;
+
+       var->grayscale          = !is_color;
+
+       var->activate           = FB_ACTIVATE_NOW;
+
+       dbg(PFX "bpp=%d, lcd_bpp=%d, "
+               "crt_enabled=%d, lcd_enabled=%d\n",
+               var->bits_per_pixel, lcd_bpp, crt_enabled, lcd_enabled);
+       dbg(PFX "xres=%d, yres=%d, vxres=%d, vyres=%d "
+               "is_color=%d, is_dual=%d, is_tft=%d\n",
+               xres, yres, xres_virtual, yres_virtual, is_color, is_dual, is_tft);
+}
+
+
+static int __devexit
+s1d13xxxfb_remove(struct device *dev)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct platform_device *pdev = to_platform_device(dev);
+       struct s1d13xxxfb_par *par = NULL;
+
+       if (info) {
+               par = info->par;
+               if (par && par->regs) {
+                       /* disable output & enable powersave */
+                       s1d13xxxfb_writereg(par, S1DREG_COM_DISP_MODE, 0x00);
+                       s1d13xxxfb_writereg(par, S1DREG_PS_CNF, 0x11);
+                       iounmap(par->regs);
+               }
+
+               fb_dealloc_cmap(&info->cmap);
+
+               if (info->screen_base)
+                       iounmap(info->screen_base);
+
+               framebuffer_release(info);
+       }
+
+       release_mem_region(pdev->resource[0].start,
+                       pdev->resource[0].end - pdev->resource[0].start +1);
+       release_mem_region(pdev->resource[1].start,
+                       pdev->resource[1].end - pdev->resource[1].start +1);
+       return 0;
+}
+
+static int __devinit
+s1d13xxxfb_probe(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct s1d13xxxfb_par *default_par;
+       struct fb_info *info;
+       struct s1d13xxxfb_pdata *pdata = NULL;
+       int ret = 0;
+       u8 revision;
+
+       dbg("probe called: device is %p\n", dev);
+
+       printk(KERN_INFO "Epson S1D13XXX FB Driver\n");
+
+       /* enable platform-dependent hardware glue, if any */
+       if (dev->platform_data)
+               pdata = dev->platform_data;
+
+       if (pdata && pdata->platform_init_video)
+               pdata->platform_init_video();
+
+
+       if (pdev->num_resources != 2) {
+               dev_err(&pdev->dev, "invalid num_resources: %i\n",
+                      pdev->num_resources);
+               ret = -ENODEV;
+               goto bail;
+       }
+
+       /* resource[0] is VRAM, resource[1] is registers */
+       if (pdev->resource[0].flags != IORESOURCE_MEM
+                       || pdev->resource[1].flags != IORESOURCE_MEM) {
+               dev_err(&pdev->dev, "invalid resource type\n");
+               ret = -ENODEV;
+               goto bail;
+       }
+
+       if (!request_mem_region(pdev->resource[0].start,
+               pdev->resource[0].end - pdev->resource[0].start +1, "s1d13xxxfb mem")) {
+               dev_dbg(dev, "request_mem_region failed\n");
+               ret = -EBUSY;
+               goto bail;
+       }
+
+       if (!request_mem_region(pdev->resource[1].start,
+               pdev->resource[1].end - pdev->resource[1].start +1, "s1d13xxxfb regs")) {
+               dev_dbg(dev, "request_mem_region failed\n");
+               ret = -EBUSY;
+               goto bail;
+       }
+
+       info = framebuffer_alloc(sizeof(struct s1d13xxxfb_par) + sizeof(u32) * 256, &pdev->dev);
+       if (!info) {
+               ret = -ENOMEM;
+               goto bail;
+       }
+
+       default_par = info->par;
+       default_par->regs = ioremap_nocache(pdev->resource[1].start,
+                       pdev->resource[1].end - pdev->resource[1].start +1);
+       if (!default_par->regs) {
+               printk(KERN_ERR PFX "unable to map registers\n");
+               ret = -ENOMEM;
+               goto bail;
+       }
+       info->pseudo_palette = default_par->pseudo_palette;
+
+       info->screen_base = ioremap_nocache(pdev->resource[0].start,
+                       pdev->resource[0].end - pdev->resource[0].start +1);
+
+       if (!info->screen_base) {
+               printk(KERN_ERR PFX "unable to map framebuffer\n");
+               ret = -ENOMEM;
+               goto bail;
+       }
+
+       revision = s1d13xxxfb_readreg(default_par, S1DREG_REV_CODE);
+       if ((revision >> 2) != S1D_CHIP_REV) {
+               printk(KERN_INFO PFX "chip not found: %i\n", (revision >> 2));
+               ret = -ENODEV;
+               goto bail;
+       }
+
+       info->fix = s1d13xxxfb_fix;
+       info->fix.mmio_start = pdev->resource[1].start;
+       info->fix.mmio_len = pdev->resource[1].end - pdev->resource[1].start +1;
+       info->fix.smem_start = pdev->resource[0].start;
+       info->fix.smem_len = pdev->resource[0].end - pdev->resource[0].start +1;
+
+       printk(KERN_INFO PFX "regs mapped at 0x%p, fb %d KiB mapped at 0x%p\n",
+              default_par->regs, info->fix.smem_len / 1024, info->screen_base);
+
+       info->par = default_par;
+       info->fbops = &s1d13xxxfb_fbops;
+       info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
+
+       /* perform "manual" chip initialization, if needed */
+       if (pdata && pdata->initregs)
+               s1d13xxxfb_runinit(info->par, pdata->initregs, pdata->initregssize);
+
+       s1d13xxxfb_fetch_hw_state(info);
+
+       if (register_framebuffer(info) < 0) {
+               ret = -EINVAL;
+               goto bail;
+       }
+
+       dev_set_drvdata(&pdev->dev, info);
+
+       printk(KERN_INFO "fb%d: %s frame buffer device\n",
+              info->node, info->fix.id);
+
+       return 0;
+
+bail:
+       s1d13xxxfb_remove(dev);
+       return ret;
+
+}
+
+#ifdef CONFIG_PM
+static int s1d13xxxfb_suspend(struct device *dev, u32 state, u32 level)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct s1d13xxxfb_par *s1dfb = info->par;
+       struct s1d13xxxfb_pdata *pdata = NULL;
+
+       /* disable display */
+       lcd_enable(s1dfb, 0);
+       crt_enable(s1dfb, 0);
+
+       if (dev->platform_data)
+               pdata = dev->platform_data;
+
+#if 0
+       if (!s1dfb->disp_save)
+               s1dfb->disp_save = kmalloc(info->fix.smem_len, GFP_KERNEL);
+
+       if (!s1dfb->disp_save) {
+               printk(KERN_ERR PFX "no memory to save screen");
+               return -ENOMEM;
+       }
+
+       memcpy_fromio(s1dfb->disp_save, info->screen_base, info->fix.smem_len);
+#else
+       s1dfb->disp_save = NULL;
+#endif
+
+       if (!s1dfb->regs_save)
+               s1dfb->regs_save = kmalloc(info->fix.mmio_len, GFP_KERNEL);
+
+       if (!s1dfb->regs_save) {
+               printk(KERN_ERR PFX "no memory to save registers");
+               return -ENOMEM;
+       }
+
+       /* backup all registers */
+       memcpy_fromio(s1dfb->regs_save, s1dfb->regs, info->fix.mmio_len);
+
+       /* now activate power save mode */
+       s1d13xxxfb_writereg(s1dfb, S1DREG_PS_CNF, 0x11);
+
+       if (pdata && pdata->platform_suspend_video)
+               return pdata->platform_suspend_video();
+       else
+               return 0;
+}
+
+static int s1d13xxxfb_resume(struct device *dev, u32 level)
+{
+       struct fb_info *info = dev_get_drvdata(dev);
+       struct s1d13xxxfb_par *s1dfb = info->par;
+       struct s1d13xxxfb_pdata *pdata = NULL;
+
+       if (level != RESUME_ENABLE)
+               return 0;
+
+       /* awaken the chip */
+       s1d13xxxfb_writereg(s1dfb, S1DREG_PS_CNF, 0x10);
+
+       /* do not let go until SDRAM "wakes up" */
+       while ((s1d13xxxfb_readreg(s1dfb, S1DREG_PS_STATUS) & 0x01))
+               udelay(10);
+
+       if (dev->platform_data)
+               pdata = dev->platform_data;
+
+       if (s1dfb->regs_save) {
+               /* will write RO regs, *should* get away with it :) */
+               memcpy_toio(s1dfb->regs, s1dfb->regs_save, info->fix.mmio_len);
+               kfree(s1dfb->regs_save);
+       }
+
+       if (s1dfb->disp_save) {
+               memcpy_toio(info->screen_base, s1dfb->disp_save,
+                               info->fix.smem_len);
+               kfree(s1dfb->disp_save);        /* XXX kmalloc()'d when? */
+       }
+
+       if ((s1dfb->display & 0x01) != 0)
+               lcd_enable(s1dfb, 1);
+       if ((s1dfb->display & 0x02) != 0)
+               crt_enable(s1dfb, 1);
+
+       if (pdata && pdata->platform_resume_video)
+               return pdata->platform_resume_video();
+       else
+               return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct device_driver s1d13xxxfb_driver = {
+       .name           = S1D_DEVICENAME,
+       .bus            = &platform_bus_type,
+       .probe          = s1d13xxxfb_probe,
+       .remove         = s1d13xxxfb_remove,
+#ifdef CONFIG_PM
+       .suspend        = s1d13xxxfb_suspend,
+       .resume         = s1d13xxxfb_resume
+#endif
+};
+
+
+static int __init
+s1d13xxxfb_init(void)
+{
+       if (fb_get_options("s1d13xxxfb", NULL))
+               return -ENODEV;
+
+       return driver_register(&s1d13xxxfb_driver);
+}
+
+
+static void __exit
+s1d13xxxfb_exit(void)
+{
+       driver_unregister(&s1d13xxxfb_driver);
+}
+
+module_init(s1d13xxxfb_init);
+module_exit(s1d13xxxfb_exit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Framebuffer driver for S1D13xxx devices");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Thibaut VARENE <varenet@parisc-linux.org>");
diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c
new file mode 100644 (file)
index 0000000..03d74e8
--- /dev/null
@@ -0,0 +1,2279 @@
+/*
+ * 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);
+       }
+}
+
+
+static 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(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
+
+       /* Clear framebuffer, it's all white in memory after boot */
+       memset_io (par->video.vbase, 0, par->video.len);
+
+       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;
+
+       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, pm_message_t 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, pci_choose_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 ************************** */
+
+static 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;
+}
+
+static 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/fs/cifs/cifsencrypt.h b/fs/cifs/cifsencrypt.h
new file mode 100644 (file)
index 0000000..03e359b
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *   fs/cifs/cifsencrypt.h
+ *
+ *   Copyright (c) International Business Machines  Corp., 2005
+ *   Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ *   Externs for misc. small encryption routines
+ *   so we do not have to put them in cifsproto.h
+ *
+ *   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
+ */
+
+/* md4.c */
+extern void mdfour(unsigned char *out, unsigned char *in, int n);
+/* smbdes.c */
+extern void E_P16(unsigned char *p14, unsigned char *p16);
+extern void E_P24(unsigned char *p21, unsigned char *c8, unsigned char *p24);
+extern void D_P16(unsigned char *p14, unsigned char *in, unsigned char *out);
+extern void E_old_pw_hash(unsigned char *, unsigned char *, unsigned char *);
+
+
+
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
new file mode 100644 (file)
index 0000000..b0ea668
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ *   fs/cifs/ioctl.c
+ *
+ *   vfs operations that deal with io control
+ *
+ *   Copyright (C) International Business Machines  Corp., 2005
+ *   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/ext2_fs.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifsfs.h"
+
+#define CIFS_IOC_CHECKUMOUNT _IO(0xCF, 2)
+
+int cifs_ioctl (struct inode * inode, struct file * filep, 
+               unsigned int command, unsigned long arg)
+{
+       int rc = -ENOTTY; /* strange error - but the precedent */
+       int xid;
+       struct cifs_sb_info *cifs_sb;
+#ifdef CONFIG_CIFS_POSIX
+       __u64   ExtAttrBits = 0;
+       __u64   ExtAttrMask = 0;
+       __u64   caps;
+       struct cifsTconInfo *tcon;
+       struct cifsFileInfo *pSMBFile =
+               (struct cifsFileInfo *)filep->private_data;
+#endif /* CONFIG_CIFS_POSIX */
+
+       xid = GetXid();
+
+        cFYI(1,("ioctl file %p  cmd %u  arg %lu",filep,command,arg));
+
+       cifs_sb = CIFS_SB(inode->i_sb);
+
+#ifdef CONFIG_CIFS_POSIX
+       tcon = cifs_sb->tcon;
+       if(tcon)
+               caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
+       else {
+               rc = -EIO;
+               FreeXid(xid);
+               return -EIO;
+       }
+#endif /* CONFIG_CIFS_POSIX */
+
+       switch(command) {
+               case CIFS_IOC_CHECKUMOUNT:
+                       cFYI(1,("User unmount attempted"));
+                       if(cifs_sb->mnt_uid == current->uid)
+                               rc = 0;
+                       else {
+                               rc = -EACCES;
+                               cFYI(1,("uids do not match"));
+                       }
+                       break;
+#ifdef CONFIG_CIFS_POSIX
+               case EXT2_IOC_GETFLAGS:
+                       if(CIFS_UNIX_EXTATTR_CAP & caps) {
+                               if (pSMBFile == NULL)
+                                       break;
+                               rc = CIFSGetExtAttr(xid, tcon, pSMBFile->netfid,
+                                       &ExtAttrBits, &ExtAttrMask);
+                               if(rc == 0)
+                                       rc = put_user(ExtAttrBits &
+                                               EXT2_FL_USER_VISIBLE,
+                                               (int __user *)arg);
+                       }
+                       break;
+
+               case EXT2_IOC_SETFLAGS:
+                       if(CIFS_UNIX_EXTATTR_CAP & caps) {
+                               if(get_user(ExtAttrBits,(int __user *)arg)) {
+                                       rc = -EFAULT;
+                                       break;
+                               }
+                               if (pSMBFile == NULL)
+                                       break;
+                               /* rc= CIFSGetExtAttr(xid,tcon,pSMBFile->netfid,
+                                       extAttrBits, &ExtAttrMask);*/
+                               
+                       }
+                       cFYI(1,("set flags not implemented yet"));
+                       break;
+#endif /* CONFIG_CIFS_POSIX */
+               default:
+                       cFYI(1,("unsupported ioctl"));
+                       break;
+       }
+
+       FreeXid(xid);
+       return rc;
+} 
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c
new file mode 100644 (file)
index 0000000..4164cd5
--- /dev/null
@@ -0,0 +1,612 @@
+/*
+ * Copyright (C) 2004, OGAWA Hirofumi
+ * Released under GPL v2.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+
+struct fatent_operations {
+       void (*ent_blocknr)(struct super_block *, int, int *, sector_t *);
+       void (*ent_set_ptr)(struct fat_entry *, int);
+       int (*ent_bread)(struct super_block *, struct fat_entry *,
+                        int, sector_t);
+       int (*ent_get)(struct fat_entry *);
+       void (*ent_put)(struct fat_entry *, int);
+       int (*ent_next)(struct fat_entry *);
+};
+
+static void fat12_ent_blocknr(struct super_block *sb, int entry,
+                             int *offset, sector_t *blocknr)
+{
+       struct msdos_sb_info *sbi = MSDOS_SB(sb);
+       int bytes = entry + (entry >> 1);
+       WARN_ON(entry < FAT_START_ENT || sbi->max_cluster <= entry);
+       *offset = bytes & (sb->s_blocksize - 1);
+       *blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits);
+}
+
+static void fat_ent_blocknr(struct super_block *sb, int entry,
+                           int *offset, sector_t *blocknr)
+{
+       struct msdos_sb_info *sbi = MSDOS_SB(sb);
+       int bytes = (entry << sbi->fatent_shift);
+       WARN_ON(entry < FAT_START_ENT || sbi->max_cluster <= entry);
+       *offset = bytes & (sb->s_blocksize - 1);
+       *blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits);
+}
+
+static void fat12_ent_set_ptr(struct fat_entry *fatent, int offset)
+{
+       struct buffer_head **bhs = fatent->bhs;
+       if (fatent->nr_bhs == 1) {
+               WARN_ON(offset >= (bhs[0]->b_size - 1));
+               fatent->u.ent12_p[0] = bhs[0]->b_data + offset;
+               fatent->u.ent12_p[1] = bhs[0]->b_data + (offset + 1);
+       } else {
+               WARN_ON(offset != (bhs[0]->b_size - 1));
+               fatent->u.ent12_p[0] = bhs[0]->b_data + offset;
+               fatent->u.ent12_p[1] = bhs[1]->b_data;
+       }
+}
+
+static void fat16_ent_set_ptr(struct fat_entry *fatent, int offset)
+{
+       WARN_ON(offset & (2 - 1));
+       fatent->u.ent16_p = (__le16 *)(fatent->bhs[0]->b_data + offset);
+}
+
+static void fat32_ent_set_ptr(struct fat_entry *fatent, int offset)
+{
+       WARN_ON(offset & (4 - 1));
+       fatent->u.ent32_p = (__le32 *)(fatent->bhs[0]->b_data + offset);
+}
+
+static int fat12_ent_bread(struct super_block *sb, struct fat_entry *fatent,
+                          int offset, sector_t blocknr)
+{
+       struct buffer_head **bhs = fatent->bhs;
+
+       WARN_ON(blocknr < MSDOS_SB(sb)->fat_start);
+       bhs[0] = sb_bread(sb, blocknr);
+       if (!bhs[0])
+               goto err;
+
+       if ((offset + 1) < sb->s_blocksize)
+               fatent->nr_bhs = 1;
+       else {
+               /* This entry is block boundary, it needs the next block */
+               blocknr++;
+               bhs[1] = sb_bread(sb, blocknr);
+               if (!bhs[1])
+                       goto err_brelse;
+               fatent->nr_bhs = 2;
+       }
+       fat12_ent_set_ptr(fatent, offset);
+       return 0;
+
+err_brelse:
+       brelse(bhs[0]);
+err:
+       printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n",
+              (unsigned long long)blocknr);
+       return -EIO;
+}
+
+static int fat_ent_bread(struct super_block *sb, struct fat_entry *fatent,
+                        int offset, sector_t blocknr)
+{
+       struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
+
+       WARN_ON(blocknr < MSDOS_SB(sb)->fat_start);
+       fatent->bhs[0] = sb_bread(sb, blocknr);
+       if (!fatent->bhs[0]) {
+               printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n",
+                      (unsigned long long)blocknr);
+               return -EIO;
+       }
+       fatent->nr_bhs = 1;
+       ops->ent_set_ptr(fatent, offset);
+       return 0;
+}
+
+static int fat12_ent_get(struct fat_entry *fatent)
+{
+       u8 **ent12_p = fatent->u.ent12_p;
+       int next;
+
+       if (fatent->entry & 1)
+               next = (*ent12_p[0] >> 4) | (*ent12_p[1] << 4);
+       else
+               next = (*ent12_p[1] << 8) | *ent12_p[0];
+       next &= 0x0fff;
+       if (next >= BAD_FAT12)
+               next = FAT_ENT_EOF;
+       return next;
+}
+
+static int fat16_ent_get(struct fat_entry *fatent)
+{
+       int next = le16_to_cpu(*fatent->u.ent16_p);
+       WARN_ON((unsigned long)fatent->u.ent16_p & (2 - 1));
+       if (next >= BAD_FAT16)
+               next = FAT_ENT_EOF;
+       return next;
+}
+
+static int fat32_ent_get(struct fat_entry *fatent)
+{
+       int next = le32_to_cpu(*fatent->u.ent32_p) & 0x0fffffff;
+       WARN_ON((unsigned long)fatent->u.ent32_p & (4 - 1));
+       if (next >= BAD_FAT32)
+               next = FAT_ENT_EOF;
+       return next;
+}
+
+static void fat12_ent_put(struct fat_entry *fatent, int new)
+{
+       u8 **ent12_p = fatent->u.ent12_p;
+
+       if (new == FAT_ENT_EOF)
+               new = EOF_FAT12;
+
+       if (fatent->entry & 1) {
+               *ent12_p[0] = (new << 4) | (*ent12_p[0] & 0x0f);
+               *ent12_p[1] = new >> 4;
+       } else {
+               *ent12_p[0] = new & 0xff;
+               *ent12_p[1] = (*ent12_p[1] & 0xf0) | (new >> 8);
+       }
+
+       mark_buffer_dirty(fatent->bhs[0]);
+       if (fatent->nr_bhs == 2)
+               mark_buffer_dirty(fatent->bhs[1]);
+}
+
+static void fat16_ent_put(struct fat_entry *fatent, int new)
+{
+       if (new == FAT_ENT_EOF)
+               new = EOF_FAT16;
+
+       *fatent->u.ent16_p = cpu_to_le16(new);
+       mark_buffer_dirty(fatent->bhs[0]);
+}
+
+static void fat32_ent_put(struct fat_entry *fatent, int new)
+{
+       if (new == FAT_ENT_EOF)
+               new = EOF_FAT32;
+
+       WARN_ON(new & 0xf0000000);
+       new |= le32_to_cpu(*fatent->u.ent32_p) & ~0x0fffffff;
+       *fatent->u.ent32_p = cpu_to_le32(new);
+       mark_buffer_dirty(fatent->bhs[0]);
+}
+
+static int fat12_ent_next(struct fat_entry *fatent)
+{
+       u8 **ent12_p = fatent->u.ent12_p;
+       struct buffer_head **bhs = fatent->bhs;
+       u8 *nextp = ent12_p[1] + 1 + (fatent->entry & 1);
+
+       fatent->entry++;
+       if (fatent->nr_bhs == 1) {
+               WARN_ON(ent12_p[0] > (u8 *)(bhs[0]->b_data + (bhs[0]->b_size - 2)));
+               WARN_ON(ent12_p[1] > (u8 *)(bhs[0]->b_data + (bhs[0]->b_size - 1)));
+               if (nextp < (u8 *)(bhs[0]->b_data + (bhs[0]->b_size - 1))) {
+                       ent12_p[0] = nextp - 1;
+                       ent12_p[1] = nextp;
+                       return 1;
+               }
+       } else {
+               WARN_ON(ent12_p[0] != (u8 *)(bhs[0]->b_data + (bhs[0]->b_size - 1)));
+               WARN_ON(ent12_p[1] != (u8 *)bhs[1]->b_data);
+               ent12_p[0] = nextp - 1;
+               ent12_p[1] = nextp;
+               brelse(bhs[0]);
+               bhs[0] = bhs[1];
+               fatent->nr_bhs = 1;
+               return 1;
+       }
+       ent12_p[0] = NULL;
+       ent12_p[1] = NULL;
+       return 0;
+}
+
+static int fat16_ent_next(struct fat_entry *fatent)
+{
+       const struct buffer_head *bh = fatent->bhs[0];
+       fatent->entry++;
+       if (fatent->u.ent16_p < (__le16 *)(bh->b_data + (bh->b_size - 2))) {
+               fatent->u.ent16_p++;
+               return 1;
+       }
+       fatent->u.ent16_p = NULL;
+       return 0;
+}
+
+static int fat32_ent_next(struct fat_entry *fatent)
+{
+       const struct buffer_head *bh = fatent->bhs[0];
+       fatent->entry++;
+       if (fatent->u.ent32_p < (__le32 *)(bh->b_data + (bh->b_size - 4))) {
+               fatent->u.ent32_p++;
+               return 1;
+       }
+       fatent->u.ent32_p = NULL;
+       return 0;
+}
+
+static struct fatent_operations fat12_ops = {
+       .ent_blocknr    = fat12_ent_blocknr,
+       .ent_set_ptr    = fat12_ent_set_ptr,
+       .ent_bread      = fat12_ent_bread,
+       .ent_get        = fat12_ent_get,
+       .ent_put        = fat12_ent_put,
+       .ent_next       = fat12_ent_next,
+};
+
+static struct fatent_operations fat16_ops = {
+       .ent_blocknr    = fat_ent_blocknr,
+       .ent_set_ptr    = fat16_ent_set_ptr,
+       .ent_bread      = fat_ent_bread,
+       .ent_get        = fat16_ent_get,
+       .ent_put        = fat16_ent_put,
+       .ent_next       = fat16_ent_next,
+};
+
+static struct fatent_operations fat32_ops = {
+       .ent_blocknr    = fat_ent_blocknr,
+       .ent_set_ptr    = fat32_ent_set_ptr,
+       .ent_bread      = fat_ent_bread,
+       .ent_get        = fat32_ent_get,
+       .ent_put        = fat32_ent_put,
+       .ent_next       = fat32_ent_next,
+};
+
+static inline void lock_fat(struct msdos_sb_info *sbi)
+{
+       down(&sbi->fat_lock);
+}
+
+static inline void unlock_fat(struct msdos_sb_info *sbi)
+{
+       up(&sbi->fat_lock);
+}
+
+void fat_ent_access_init(struct super_block *sb)
+{
+       struct msdos_sb_info *sbi = MSDOS_SB(sb);
+
+       init_MUTEX(&sbi->fat_lock);
+
+       switch (sbi->fat_bits) {
+       case 32:
+               sbi->fatent_shift = 2;
+               sbi->fatent_ops = &fat32_ops;
+               break;
+       case 16:
+               sbi->fatent_shift = 1;
+               sbi->fatent_ops = &fat16_ops;
+               break;
+       case 12:
+               sbi->fatent_shift = -1;
+               sbi->fatent_ops = &fat12_ops;
+               break;
+       }
+}
+
+static inline int fat_ent_update_ptr(struct super_block *sb,
+                                    struct fat_entry *fatent,
+                                    int offset, sector_t blocknr)
+{
+       struct msdos_sb_info *sbi = MSDOS_SB(sb);
+       struct fatent_operations *ops = sbi->fatent_ops;
+       struct buffer_head **bhs = fatent->bhs;
+
+       /* Is this fatent's blocks including this entry? */
+       if (!fatent->nr_bhs || bhs[0]->b_blocknr != blocknr)
+               return 0;
+       /* Does this entry need the next block? */
+       if (sbi->fat_bits == 12 && (offset + 1) >= sb->s_blocksize) {
+               if (fatent->nr_bhs != 2 || bhs[1]->b_blocknr != (blocknr + 1))
+                       return 0;
+       }
+       ops->ent_set_ptr(fatent, offset);
+       return 1;
+}
+
+int fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry)
+{
+       struct super_block *sb = inode->i_sb;
+       struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
+       struct fatent_operations *ops = sbi->fatent_ops;
+       int err, offset;
+       sector_t blocknr;
+
+       if (entry < FAT_START_ENT || sbi->max_cluster <= entry) {
+               fatent_brelse(fatent);
+               fat_fs_panic(sb, "invalid access to FAT (entry 0x%08x)", entry);
+               return -EIO;
+       }
+
+       fatent_set_entry(fatent, entry);
+       ops->ent_blocknr(sb, entry, &offset, &blocknr);
+
+       if (!fat_ent_update_ptr(sb, fatent, offset, blocknr)) {
+               fatent_brelse(fatent);
+               err = ops->ent_bread(sb, fatent, offset, blocknr);
+               if (err)
+                       return err;
+       }
+       return ops->ent_get(fatent);
+}
+
+/* FIXME: We can write the blocks as more big chunk. */
+static int fat_mirror_bhs(struct super_block *sb, struct buffer_head **bhs,
+                         int nr_bhs)
+{
+       struct msdos_sb_info *sbi = MSDOS_SB(sb);
+       struct buffer_head *c_bh;
+       int err, n, copy;
+
+       err = 0;
+       for (copy = 1; copy < sbi->fats; copy++) {
+               sector_t backup_fat = sbi->fat_length * copy;
+
+               for (n = 0; n < nr_bhs; n++) {
+                       c_bh = sb_getblk(sb, backup_fat + bhs[n]->b_blocknr);
+                       if (!c_bh) {
+                               err = -ENOMEM;
+                               goto error;
+                       }
+                       memcpy(c_bh->b_data, bhs[n]->b_data, sb->s_blocksize);
+                       set_buffer_uptodate(c_bh);
+                       mark_buffer_dirty(c_bh);
+                       if (sb->s_flags & MS_SYNCHRONOUS)
+                               err = sync_dirty_buffer(c_bh);
+                       brelse(c_bh);
+                       if (err)
+                               goto error;
+               }
+       }
+error:
+       return err;
+}
+
+int fat_ent_write(struct inode *inode, struct fat_entry *fatent,
+                 int new, int wait)
+{
+       struct super_block *sb = inode->i_sb;
+       struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
+       int err;
+
+       ops->ent_put(fatent, new);
+       if (wait) {
+               err = fat_sync_bhs(fatent->bhs, fatent->nr_bhs);
+               if (err)
+                       return err;
+       }
+       return fat_mirror_bhs(sb, fatent->bhs, fatent->nr_bhs);
+}
+
+static inline int fat_ent_next(struct msdos_sb_info *sbi,
+                              struct fat_entry *fatent)
+{
+       if (sbi->fatent_ops->ent_next(fatent)) {
+               if (fatent->entry < sbi->max_cluster)
+                       return 1;
+       }
+       return 0;
+}
+
+static inline int fat_ent_read_block(struct super_block *sb,
+                                    struct fat_entry *fatent)
+{
+       struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
+       sector_t blocknr;
+       int offset;
+
+       fatent_brelse(fatent);
+       ops->ent_blocknr(sb, fatent->entry, &offset, &blocknr);
+       return ops->ent_bread(sb, fatent, offset, blocknr);
+}
+
+static void fat_collect_bhs(struct buffer_head **bhs, int *nr_bhs,
+                           struct fat_entry *fatent)
+{
+       int n, i;
+
+       for (n = 0; n < fatent->nr_bhs; n++) {
+               for (i = 0; i < *nr_bhs; i++) {
+                       if (fatent->bhs[n] == bhs[i])
+                               break;
+               }
+               if (i == *nr_bhs) {
+                       get_bh(fatent->bhs[n]);
+                       bhs[i] = fatent->bhs[n];
+                       (*nr_bhs)++;
+               }
+       }
+}
+
+int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster)
+{
+       struct super_block *sb = inode->i_sb;
+       struct msdos_sb_info *sbi = MSDOS_SB(sb);
+       struct fatent_operations *ops = sbi->fatent_ops;
+       struct fat_entry fatent, prev_ent;
+       struct buffer_head *bhs[MAX_BUF_PER_PAGE];
+       int i, count, err, nr_bhs, idx_clus;
+
+       BUG_ON(nr_cluster > (MAX_BUF_PER_PAGE / 2));    /* fixed limit */
+
+       lock_fat(sbi);
+       if (sbi->free_clusters != -1 && sbi->free_clusters < nr_cluster) {
+               unlock_fat(sbi);
+               return -ENOSPC;
+       }
+
+       err = nr_bhs = idx_clus = 0;
+       count = FAT_START_ENT;
+       fatent_init(&prev_ent);
+       fatent_init(&fatent);
+       fatent_set_entry(&fatent, sbi->prev_free + 1);
+       while (count < sbi->max_cluster) {
+               if (fatent.entry >= sbi->max_cluster)
+                       fatent.entry = FAT_START_ENT;
+               fatent_set_entry(&fatent, fatent.entry);
+               err = fat_ent_read_block(sb, &fatent);
+               if (err)
+                       goto out;
+
+               /* Find the free entries in a block */
+               do {
+                       if (ops->ent_get(&fatent) == FAT_ENT_FREE) {
+                               int entry = fatent.entry;
+
+                               /* make the cluster chain */
+                               ops->ent_put(&fatent, FAT_ENT_EOF);
+                               if (prev_ent.nr_bhs)
+                                       ops->ent_put(&prev_ent, entry);
+
+                               fat_collect_bhs(bhs, &nr_bhs, &fatent);
+
+                               sbi->prev_free = entry;
+                               if (sbi->free_clusters != -1)
+                                       sbi->free_clusters--;
+
+                               cluster[idx_clus] = entry;
+                               idx_clus++;
+                               if (idx_clus == nr_cluster)
+                                       goto out;
+
+                               /*
+                                * fat_collect_bhs() gets ref-count of bhs,
+                                * so we can still use the prev_ent.
+                                */
+                               prev_ent = fatent;
+                       }
+                       count++;
+                       if (count == sbi->max_cluster)
+                               break;
+               } while (fat_ent_next(sbi, &fatent));
+       }
+
+       /* Couldn't allocate the free entries */
+       sbi->free_clusters = 0;
+       err = -ENOSPC;
+
+out:
+       unlock_fat(sbi);
+       fatent_brelse(&fatent);
+       if (!err) {
+               if (inode_needs_sync(inode))
+                       err = fat_sync_bhs(bhs, nr_bhs);
+               if (!err)
+                       err = fat_mirror_bhs(sb, bhs, nr_bhs);
+       }
+       for (i = 0; i < nr_bhs; i++)
+               brelse(bhs[i]);
+       fat_clusters_flush(sb);
+
+       if (err && idx_clus)
+               fat_free_clusters(inode, cluster[0]);
+
+       return err;
+}
+
+int fat_free_clusters(struct inode *inode, int cluster)
+{
+       struct super_block *sb = inode->i_sb;
+       struct msdos_sb_info *sbi = MSDOS_SB(sb);
+       struct fatent_operations *ops = sbi->fatent_ops;
+       struct fat_entry fatent;
+       struct buffer_head *bhs[MAX_BUF_PER_PAGE];
+       int i, err, nr_bhs;
+
+       nr_bhs = 0;
+       fatent_init(&fatent);
+       lock_fat(sbi);
+       do {
+               cluster = fat_ent_read(inode, &fatent, cluster);
+               if (cluster < 0) {
+                       err = cluster;
+                       goto error;
+               } else if (cluster == FAT_ENT_FREE) {
+                       fat_fs_panic(sb, "%s: deleting FAT entry beyond EOF",
+                                    __FUNCTION__);
+                       err = -EIO;
+                       goto error;
+               }
+
+               ops->ent_put(&fatent, FAT_ENT_FREE);
+               if (sbi->free_clusters != -1)
+                       sbi->free_clusters++;
+
+               if (nr_bhs + fatent.nr_bhs > MAX_BUF_PER_PAGE) {
+                       if (sb->s_flags & MS_SYNCHRONOUS) {
+                               err = fat_sync_bhs(bhs, nr_bhs);
+                               if (err)
+                                       goto error;
+                       }
+                       err = fat_mirror_bhs(sb, bhs, nr_bhs);
+                       if (err)
+                               goto error;
+                       for (i = 0; i < nr_bhs; i++)
+                               brelse(bhs[i]);
+                       nr_bhs = 0;
+               }
+               fat_collect_bhs(bhs, &nr_bhs, &fatent);
+       } while (cluster != FAT_ENT_EOF);
+
+       if (sb->s_flags & MS_SYNCHRONOUS) {
+               err = fat_sync_bhs(bhs, nr_bhs);
+               if (err)
+                       goto error;
+       }
+       err = fat_mirror_bhs(sb, bhs, nr_bhs);
+error:
+       fatent_brelse(&fatent);
+       for (i = 0; i < nr_bhs; i++)
+               brelse(bhs[i]);
+       unlock_fat(sbi);
+
+       fat_clusters_flush(sb);
+
+       return err;
+}
+
+EXPORT_SYMBOL(fat_free_clusters);
+
+int fat_count_free_clusters(struct super_block *sb)
+{
+       struct msdos_sb_info *sbi = MSDOS_SB(sb);
+       struct fatent_operations *ops = sbi->fatent_ops;
+       struct fat_entry fatent;
+       int err = 0, free;
+
+       lock_fat(sbi);
+       if (sbi->free_clusters != -1)
+               goto out;
+
+       free = 0;
+       fatent_init(&fatent);
+       fatent_set_entry(&fatent, FAT_START_ENT);
+       while (fatent.entry < sbi->max_cluster) {
+               err = fat_ent_read_block(sb, &fatent);
+               if (err)
+                       goto out;
+
+               do {
+                       if (ops->ent_get(&fatent) == FAT_ENT_FREE)
+                               free++;
+               } while (fat_ent_next(sbi, &fatent));
+       }
+       sbi->free_clusters = free;
+       fatent_brelse(&fatent);
+out:
+       unlock_fat(sbi);
+       return err;
+}
diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h
new file mode 100644 (file)
index 0000000..9ce7b51
--- /dev/null
@@ -0,0 +1,190 @@
+#include <linux/fs.h>
+#include <linux/buffer_head.h>
+#include <linux/iso_fs.h>
+#include <asm/unaligned.h>
+
+enum isofs_file_format {
+       isofs_file_normal = 0,
+       isofs_file_sparse = 1,
+       isofs_file_compressed = 2,
+};
+       
+/*
+ * iso fs inode data in memory
+ */
+struct iso_inode_info {
+       unsigned long i_iget5_block;
+       unsigned long i_iget5_offset;
+       unsigned int i_first_extent;
+       unsigned char i_file_format;
+       unsigned char i_format_parm[3];
+       unsigned long i_next_section_block;
+       unsigned long i_next_section_offset;
+       off_t i_section_size;
+       struct inode vfs_inode;
+};
+
+/*
+ * iso9660 super-block data in memory
+ */
+struct isofs_sb_info {
+       unsigned long s_ninodes;
+       unsigned long s_nzones;
+       unsigned long s_firstdatazone;
+       unsigned long s_log_zone_size;
+       unsigned long s_max_size;
+       
+       unsigned char s_high_sierra; /* A simple flag */
+       unsigned char s_mapping;
+       int           s_rock_offset; /* offset of SUSP fields within SU area */
+       unsigned char s_rock;
+       unsigned char s_joliet_level;
+       unsigned char s_utf8;
+       unsigned char s_cruft; /* Broken disks with high
+                                 byte of length containing
+                                 junk */
+       unsigned char s_unhide;
+       unsigned char s_nosuid;
+       unsigned char s_nodev;
+       unsigned char s_nocompress;
+
+       mode_t s_mode;
+       gid_t s_gid;
+       uid_t s_uid;
+       struct nls_table *s_nls_iocharset; /* Native language support table */
+};
+
+static inline struct isofs_sb_info *ISOFS_SB(struct super_block *sb)
+{
+       return sb->s_fs_info;
+}
+
+static inline struct iso_inode_info *ISOFS_I(struct inode *inode)
+{
+       return container_of(inode, struct iso_inode_info, vfs_inode);
+}
+
+static inline int isonum_711(char *p)
+{
+       return *(u8 *)p;
+}
+static inline int isonum_712(char *p)
+{
+       return *(s8 *)p;
+}
+static inline unsigned int isonum_721(char *p)
+{
+       return le16_to_cpu(get_unaligned((__le16 *)p));
+}
+static inline unsigned int isonum_722(char *p)
+{
+       return be16_to_cpu(get_unaligned((__le16 *)p));
+}
+static inline unsigned int isonum_723(char *p)
+{
+       /* Ignore bigendian datum due to broken mastering programs */
+       return le16_to_cpu(get_unaligned((__le16 *)p));
+}
+static inline unsigned int isonum_731(char *p)
+{
+       return le32_to_cpu(get_unaligned((__le32 *)p));
+}
+static inline unsigned int isonum_732(char *p)
+{
+       return be32_to_cpu(get_unaligned((__le32 *)p));
+}
+static inline unsigned int isonum_733(char *p)
+{
+       /* Ignore bigendian datum due to broken mastering programs */
+       return le32_to_cpu(get_unaligned((__le32 *)p));
+}
+extern int iso_date(char *, int);
+
+struct inode;          /* To make gcc happy */
+
+extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *);
+extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *);
+extern int isofs_name_translate(struct iso_directory_record *, char *, struct inode *);
+
+int get_joliet_filename(struct iso_directory_record *, unsigned char *, struct inode *);
+int get_acorn_filename(struct iso_directory_record *, char *, struct inode *);
+
+extern struct dentry *isofs_lookup(struct inode *, struct dentry *, struct nameidata *);
+extern struct buffer_head *isofs_bread(struct inode *, sector_t);
+extern int isofs_get_blocks(struct inode *, sector_t, struct buffer_head **, unsigned long);
+
+extern struct inode *isofs_iget(struct super_block *sb,
+                                unsigned long block,
+                                unsigned long offset);
+
+/* Because the inode number is no longer relevant to finding the
+ * underlying meta-data for an inode, we are free to choose a more
+ * convenient 32-bit number as the inode number.  The inode numbering
+ * scheme was recommended by Sergey Vlasov and Eric Lammerts. */
+static inline unsigned long isofs_get_ino(unsigned long block,
+                                         unsigned long offset,
+                                         unsigned long bufbits)
+{
+       return (block << (bufbits - 5)) | (offset >> 5);
+}
+
+/* Every directory can have many redundant directory entries scattered
+ * throughout the directory tree.  First there is the directory entry
+ * with the name of the directory stored in the parent directory.
+ * Then, there is the "." directory entry stored in the directory
+ * itself.  Finally, there are possibly many ".." directory entries
+ * stored in all the subdirectories.
+ *
+ * In order for the NFS get_parent() method to work and for the
+ * general consistency of the dcache, we need to make sure the
+ * "i_iget5_block" and "i_iget5_offset" all point to exactly one of
+ * the many redundant entries for each directory.  We normalize the
+ * block and offset by always making them point to the "."  directory.
+ *
+ * Notice that we do not use the entry for the directory with the name
+ * that is located in the parent directory.  Even though choosing this
+ * first directory is more natural, it is much easier to find the "."
+ * entry in the NFS get_parent() method because it is implicitly
+ * encoded in the "extent + ext_attr_length" fields of _all_ the
+ * redundant entries for the directory.  Thus, it can always be
+ * reached regardless of which directory entry you have in hand.
+ *
+ * This works because the "." entry is simply the first directory
+ * record when you start reading the file that holds all the directory
+ * records, and this file starts at "extent + ext_attr_length" blocks.
+ * Because the "." entry is always the first entry listed in the
+ * directories file, the normalized "offset" value is always 0.
+ *
+ * You should pass the directory entry in "de".  On return, "block"
+ * and "offset" will hold normalized values.  Only directories are
+ * affected making it safe to call even for non-directory file
+ * types. */
+static inline void
+isofs_normalize_block_and_offset(struct iso_directory_record* de,
+                                unsigned long *block,
+                                unsigned long *offset)
+{
+       /* Only directories are normalized. */
+       if (de->flags[0] & 2) {
+               *offset = 0;
+               *block = (unsigned long)isonum_733(de->extent)
+                       + (unsigned long)isonum_711(de->ext_attr_length);
+       }
+}
+
+extern struct inode_operations isofs_dir_inode_operations;
+extern struct file_operations isofs_dir_operations;
+extern struct address_space_operations isofs_symlink_aops;
+extern struct export_operations isofs_export_ops;
+
+/* The following macros are used to check for memory leaks. */
+#ifdef LEAK_CHECK
+#define free_s leak_check_free_s
+#define malloc leak_check_malloc
+#define sb_bread leak_check_bread
+#define brelse leak_check_brelse
+extern void * leak_check_malloc(unsigned int size);
+extern void leak_check_free_s(void * obj, int size);
+extern struct buffer_head * leak_check_bread(struct super_block *sb, int block);
+extern void leak_check_brelse(struct buffer_head * bh);
+#endif /* LEAK_CHECK */
diff --git a/fs/xfs/linux-2.6/xfs_export.h b/fs/xfs/linux-2.6/xfs_export.h
new file mode 100644 (file)
index 0000000..60b2aba
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 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/
+ */
+#ifndef __XFS_EXPORT_H__
+#define __XFS_EXPORT_H__
+
+/*
+ * Common defines for code related to exporting XFS filesystems over NFS.
+ *
+ * The NFS fileid goes out on the wire as an array of
+ * 32bit unsigned ints in host order.  There are 5 possible
+ * formats.
+ *
+ * (1) fileid_type=0x00
+ *     (no fileid data; handled by the generic code)
+ *
+ * (2) fileid_type=0x01
+ *     inode-num
+ *     generation
+ *
+ * (3) fileid_type=0x02
+ *     inode-num
+ *     generation
+ *     parent-inode-num
+ *     parent-generation
+ *
+ * (4) fileid_type=0x81
+ *     inode-num-lo32
+ *     inode-num-hi32
+ *     generation
+ *
+ * (5) fileid_type=0x82
+ *     inode-num-lo32
+ *     inode-num-hi32
+ *     generation
+ *     parent-inode-num-lo32
+ *     parent-inode-num-hi32
+ *     parent-generation
+ *
+ * Note, the NFS filehandle also includes an fsid portion which
+ * may have an inode number in it.  That number is hardcoded to
+ * 32bits and there is no way for XFS to intercept it.  In
+ * practice this means when exporting an XFS filesytem with 64bit
+ * inodes you should either export the mountpoint (rather than
+ * a subdirectory) or use the "fsid" export option.
+ */
+
+/* This flag goes on the wire.  Don't play with it. */
+#define XFS_FILEID_TYPE_64FLAG 0x80    /* NFS fileid has 64bit inodes */
+
+/* Calculate the length in u32 units of the fileid data */
+static inline int
+xfs_fileid_length(int hasparent, int is64)
+{
+       return hasparent ? (is64 ? 6 : 4) : (is64 ? 3 : 2);
+}
+
+/*
+ * Decode encoded inode information (either for the inode itself
+ * or the parent) into an xfs_fid2_t structure.  Advances and
+ * returns the new data pointer
+ */
+static inline __u32 *
+xfs_fileid_decode_fid2(__u32 *p, xfs_fid2_t *fid, int is64)
+{
+       fid->fid_len = sizeof(xfs_fid2_t) - sizeof(fid->fid_len);
+       fid->fid_pad = 0;
+       fid->fid_ino = *p++;
+#if XFS_BIG_INUMS
+       if (is64)
+               fid->fid_ino |= (((__u64)(*p++)) << 32);
+#endif
+       fid->fid_gen = *p++;
+       return p;
+}
+
+/*
+ * Encode inode information (either for the inode itself or the
+ * parent) into a fileid buffer.  Advances and returns the new
+ * data pointer.
+ */
+static inline __u32 *
+xfs_fileid_encode_inode(__u32 *p, struct inode *inode, int is64)
+{
+       *p++ = (__u32)inode->i_ino;
+#if XFS_BIG_INUMS
+       if (is64)
+               *p++ = (__u32)(inode->i_ino >> 32);
+#endif
+       *p++ = inode->i_generation;
+       return p;
+}
+
+#endif /* __XFS_EXPORT_H__ */
diff --git a/include/asm-arm/arch-omap/aic23.h b/include/asm-arm/arch-omap/aic23.h
new file mode 100644 (file)
index 0000000..590bac2
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * linux/include/asm-arm/arch-omap/aic23.h
+ *
+ * Hardware definitions for TI TLV320AIC23 audio codec
+ *
+ * Copyright (C) 2002 RidgeRun, Inc.
+ * Author: Steve Johnson
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the 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_AIC23_H
+#define __ASM_ARCH_AIC23_H
+
+// Codec TLV320AIC23
+#define LEFT_LINE_VOLUME_ADDR          0x00
+#define RIGHT_LINE_VOLUME_ADDR         0x01
+#define LEFT_CHANNEL_VOLUME_ADDR       0x02
+#define RIGHT_CHANNEL_VOLUME_ADDR      0x03
+#define ANALOG_AUDIO_CONTROL_ADDR      0x04
+#define DIGITAL_AUDIO_CONTROL_ADDR     0x05
+#define POWER_DOWN_CONTROL_ADDR                0x06
+#define DIGITAL_AUDIO_FORMAT_ADDR      0x07
+#define SAMPLE_RATE_CONTROL_ADDR       0x08
+#define DIGITAL_INTERFACE_ACT_ADDR     0x09
+#define RESET_CONTROL_ADDR             0x0F
+
+// Left (right) line input volume control register
+#define LRS_ENABLED                    0x0100
+#define LIM_MUTED                      0x0080
+#define LIV_DEFAULT                    0x0017
+#define LIV_MAX                                0x001f
+#define LIV_MIN                                0x0000
+
+// Left (right) channel headphone volume control register
+#define LZC_ON                         0x0080
+#define LHV_DEFAULT                    0x0079
+#define LHV_MAX                                0x007f
+#define LHV_MIN                                0x0000
+
+// Analog audio path control register
+#define STE_ENABLED                    0x0020
+#define DAC_SELECTED                   0x0010
+#define BYPASS_ON                      0x0008
+#define INSEL_MIC                      0x0004
+#define MICM_MUTED                     0x0002
+#define MICB_20DB                      0x0001
+
+// Digital audio path control register
+#define DACM_MUTE                      0x0008
+#define DEEMP_32K                      0x0002
+#define DEEMP_44K                      0x0004
+#define DEEMP_48K                      0x0006
+#define ADCHP_ON                       0x0001
+
+// Power control down register
+#define DEVICE_POWER_OFF               0x0080
+#define CLK_OFF                                0x0040
+#define OSC_OFF                                0x0020
+#define OUT_OFF                                0x0010
+#define DAC_OFF                                0x0008
+#define ADC_OFF                                0x0004
+#define MIC_OFF                                0x0002
+#define LINE_OFF                       0x0001
+
+// Digital audio interface register
+#define MS_MASTER                      0x0040
+#define LRSWAP_ON                      0x0020
+#define LRP_ON                         0x0010
+#define IWL_16                         0x0000
+#define IWL_20                         0x0004
+#define IWL_24                         0x0008
+#define IWL_32                         0x000C
+#define FOR_I2S                                0x0002
+#define FOR_DSP                                0x0003
+
+// Sample rate control register
+#define CLKOUT_HALF                    0x0080
+#define CLKIN_HALF                     0x0040
+#define BOSR_384fs                     0x0002 // BOSR_272fs when in USB mode
+#define USB_CLK_ON                     0x0001
+#define SR_MASK                         0xf
+#define CLKOUT_SHIFT                    7
+#define CLKIN_SHIFT                     6
+#define SR_SHIFT                        2
+#define BOSR_SHIFT                      1
+
+// Digital interface register
+#define ACT_ON                         0x0001
+
+#define TLV320AIC23ID1                  (0x1a) // cs low
+#define TLV320AIC23ID2                  (0x1b) // cs high
+
+#endif /* __ASM_ARCH_AIC23_H */
diff --git a/include/asm-arm/arch-omap/board-netstar.h b/include/asm-arm/arch-omap/board-netstar.h
new file mode 100644 (file)
index 0000000..77cc0fb
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2004 2N Telekomunikace, Ladislav Michl <michl@2n.cz>
+ *
+ * Hardware definitions for OMAP5910 based NetStar 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.
+ */
+
+#ifndef __ASM_ARCH_NETSTAR_H
+#define __ASM_ARCH_NETSTAR_H
+
+#include <asm/arch/tc.h>
+
+#define OMAP_NAND_FLASH_START1         OMAP_CS1_PHYS + (1 << 23)
+#define OMAP_NAND_FLASH_START2         OMAP_CS1_PHYS + (2 << 23)
+
+#endif /*  __ASM_ARCH_NETSTAR_H */
diff --git a/include/asm-arm/arch-pxa/poodle.h b/include/asm-arm/arch-pxa/poodle.h
new file mode 100644 (file)
index 0000000..58bda9d
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * linux/include/asm-arm/arch-pxa/poodle.h
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ * Based on:
+ *   linux/include/asm-arm/arch-sa1100/collie.h
+ *
+ * ChangeLog:
+ *   04-06-2001 Lineo Japan, Inc.
+ *   04-16-2001 SHARP Corporation
+ *   Update to 2.6 John Lenz
+ */
+#ifndef __ASM_ARCH_POODLE_H
+#define __ASM_ARCH_POODLE_H  1
+
+/*
+ * GPIOs
+ */
+/* PXA GPIOs */
+#define POODLE_GPIO_ON_KEY             (0)
+#define POODLE_GPIO_AC_IN              (1)
+#define POODLE_GPIO_CO                 16
+#define POODLE_GPIO_TP_INT             (5)
+#define POODLE_GPIO_WAKEUP             (11)    /* change battery */
+#define POODLE_GPIO_GA_INT             (10)
+#define POODLE_GPIO_IR_ON              (22)
+#define POODLE_GPIO_HP_IN              (4)
+#define POODLE_GPIO_CF_IRQ             (17)
+#define POODLE_GPIO_CF_CD              (14)
+#define POODLE_GPIO_CF_STSCHG          (14)
+#define POODLE_GPIO_SD_PWR             (33)
+#define POODLE_GPIO_nSD_CLK            (6)
+#define POODLE_GPIO_nSD_WP             (7)
+#define POODLE_GPIO_nSD_INT            (8)
+#define POODLE_GPIO_nSD_DETECT         (9)
+#define POODLE_GPIO_MAIN_BAT_LOW       (13)
+#define POODLE_GPIO_BAT_COVER          (13)
+#define POODLE_GPIO_ADC_TEMP_ON                (21)
+#define POODLE_GPIO_BYPASS_ON          (36)
+#define POODLE_GPIO_CHRG_ON            (38)
+#define POODLE_GPIO_CHRG_FULL          (16)
+
+/* PXA GPIOs */
+#define POODLE_IRQ_GPIO_ON_KEY         IRQ_GPIO0
+#define POODLE_IRQ_GPIO_AC_IN          IRQ_GPIO1
+#define POODLE_IRQ_GPIO_HP_IN          IRQ_GPIO4
+#define POODLE_IRQ_GPIO_CO             IRQ_GPIO16
+#define POODLE_IRQ_GPIO_TP_INT         IRQ_GPIO5
+#define POODLE_IRQ_GPIO_WAKEUP         IRQ_GPIO11
+#define POODLE_IRQ_GPIO_GA_INT         IRQ_GPIO10
+#define POODLE_IRQ_GPIO_CF_IRQ         IRQ_GPIO17
+#define POODLE_IRQ_GPIO_CF_CD          IRQ_GPIO14
+#define POODLE_IRQ_GPIO_nSD_INT                IRQ_GPIO8
+#define POODLE_IRQ_GPIO_nSD_DETECT     IRQ_GPIO9
+#define POODLE_IRQ_GPIO_MAIN_BAT_LOW   IRQ_GPIO13
+
+/* SCOOP GPIOs */
+#define POODLE_SCOOP_CHARGE_ON SCOOP_GPCR_PA11
+#define POODLE_SCOOP_CP401     SCOOP_GPCR_PA13
+#define POODLE_SCOOP_VPEN      SCOOP_GPCR_PA18
+#define POODLE_SCOOP_L_PCLK    SCOOP_GPCR_PA20
+#define POODLE_SCOOP_L_LCLK    SCOOP_GPCR_PA21
+#define POODLE_SCOOP_HS_OUT    SCOOP_GPCR_PA22
+
+#define POODLE_SCOOP_IO_DIR    ( POODLE_SCOOP_VPEN | POODLE_SCOOP_HS_OUT )
+#define POODLE_SCOOP_IO_OUT    ( 0 )
+
+#endif /* __ASM_ARCH_POODLE_H  */
diff --git a/include/asm-arm/arch-s3c2410/regs-adc.h b/include/asm-arm/arch-s3c2410/regs-adc.h
new file mode 100644 (file)
index 0000000..15bfc2f
--- /dev/null
@@ -0,0 +1,63 @@
+/* linux/include/asm/arch-s3c2410/regs-adc.h
+ *
+ * Copyright (c) 2004 Shannon Holland <holland@loser.net>
+ *
+ * This program is free software; yosu 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 ADC registers
+ *
+ *  Changelog:
+ *    27-09-2004     SAH     Created file
+*/
+
+#ifndef __ASM_ARCH_REGS_ADC_H
+#define __ASM_ARCH_REGS_ADC_H "regs-adc.h"
+
+#define S3C2410_ADCREG(x) (x)
+
+#define S3C2410_ADCCON    S3C2410_ADCREG(0x00)
+#define S3C2410_ADCTSC    S3C2410_ADCREG(0x04)
+#define S3C2410_ADCDLY    S3C2410_ADCREG(0x08)
+#define S3C2410_ADCDAT0           S3C2410_ADCREG(0x0C)
+#define S3C2410_ADCDAT1           S3C2410_ADCREG(0x10)
+
+
+/* ADCCON Register Bits */
+#define S3C2410_ADCCON_ECFLG           (1<<15)
+#define S3C2410_ADCCON_PRSCEN          (1<<14)
+#define S3C2410_ADCCON_PRSCVL(x)       (((x)&0xFF)<<6)
+#define S3C2410_ADCCON_PRSCVLMASK      (0xFF<<6)
+#define S3C2410_ADCCON_SELMUX(x)       (((x)&0x7)<<3)
+#define S3C2410_ADCCON_MUXMASK         (0x7<<3)
+#define S3C2410_ADCCON_STDBM           (1<<2)
+#define S3C2410_ADCCON_READ_START      (1<<1)
+#define S3C2410_ADCCON_ENABLE_START    (1<<0)
+#define S3C2410_ADCCON_STARTMASK       (0x3<<0)
+
+
+/* ADCTSC Register Bits */
+#define S3C2410_ADCTSC_YM_SEN          (1<<7)
+#define S3C2410_ADCTSC_YP_SEN          (1<<6)
+#define S3C2410_ADCTSC_XM_SEN          (1<<5)
+#define S3C2410_ADCTSC_XP_SEN          (1<<4)
+#define S3C2410_ADCTSC_PULL_UP_DISABLE (1<<3)
+#define S3C2410_ADCTSC_AUTO_PST                (1<<2)
+#define S3C2410_ADCTSC_XY_PST          (0x3<<0)
+
+/* ADCDAT0 Bits */
+#define S3C2410_ADCDAT0_UPDOWN         (1<<15)
+#define S3C2410_ADCDAT0_AUTO_PST       (1<<14)
+#define S3C2410_ADCDAT0_XY_PST         (0x3<<12)
+#define S3C2410_ADCDAT0_XPDATA_MASK    (0x03FF)
+
+/* ADCDAT1 Bits */
+#define S3C2410_ADCDAT1_UPDOWN         (1<<15)
+#define S3C2410_ADCDAT1_AUTO_PST       (1<<14)
+#define S3C2410_ADCDAT1_XY_PST         (0x3<<12)
+#define S3C2410_ADCDAT1_YPDATA_MASK    (0x03FF)
+
+#endif /* __ASM_ARCH_REGS_ADC_H */
+
+
diff --git a/include/asm-generic/signal.h b/include/asm-generic/signal.h
new file mode 100644 (file)
index 0000000..9418d6e
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef SIG_BLOCK
+#define SIG_BLOCK          0   /* for blocking signals */
+#endif
+#ifndef SIG_UNBLOCK
+#define SIG_UNBLOCK        1   /* for unblocking signals */
+#endif
+#ifndef SIG_SETMASK
+#define SIG_SETMASK        2   /* for setting the signal mask */
+#endif
+
+#ifndef __ASSEMBLY__
+typedef void __signalfn_t(int);
+typedef __signalfn_t __user *__sighandler_t;
+
+typedef void __restorefn_t(void);
+typedef __restorefn_t __user *__sigrestore_t;
+
+#define SIG_DFL        ((__force __sighandler_t)0)     /* default signal handling */
+#define SIG_IGN        ((__force __sighandler_t)1)     /* ignore signal */
+#define SIG_ERR        ((__force __sighandler_t)-1)    /* error return from signal */
+#endif
diff --git a/include/asm-ia64/sn/pcibus_provider_defs.h b/include/asm-ia64/sn/pcibus_provider_defs.h
new file mode 100644 (file)
index 0000000..04e27d5
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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
+#define PCIIO_ASIC_TYPE_TIOCA  4
+
+#define PCIIO_ASIC_MAX_TYPES   5
+
+/*
+ * 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;
+};
+
+/*
+ * SN pci bus indirection
+ */
+
+struct sn_pcibus_provider {
+       dma_addr_t      (*dma_map)(struct pci_dev *, unsigned long, size_t);
+       dma_addr_t      (*dma_map_consistent)(struct pci_dev *, unsigned long, size_t);
+       void            (*dma_unmap)(struct pci_dev *, dma_addr_t, int);
+       void *          (*bus_fixup)(struct pcibus_bussoft *);
+};
+
+extern struct sn_pcibus_provider *sn_pci_provider[];
+#endif                         /* _ASM_IA64_SN_PCI_PCIBUS_PROVIDER_H */
diff --git a/include/asm-ia64/sn/pcidev.h b/include/asm-ia64/sn/pcidev.h
new file mode 100644 (file)
index 0000000..ed4031d
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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 SN_PCIDEV_BUSPROVIDER(pci_dev) \
+       (SN_PCIDEV_INFO(pci_dev)->pdi_provider)
+
+#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;
+       struct sn_pcibus_provider *pdi_provider;        /* sn pci ops */
+};
+
+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/include/asm-ia64/sn/tioca_provider.h b/include/asm-ia64/sn/tioca_provider.h
new file mode 100644 (file)
index 0000000..b6acc22
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * 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-2005 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#ifndef _ASM_IA64_SN_TIO_CA_AGP_PROVIDER_H
+#define _ASM_IA64_SN_TIO_CA_AGP_PROVIDER_H
+
+#include <asm/sn/tioca.h>
+
+/*
+ * WAR enables
+ * Defines for individual WARs. Each is a bitmask of applicable
+ * part revision numbers. (1 << 1) == rev A, (1 << 2) == rev B,
+ * (3 << 1) == (rev A or rev B), etc
+ */
+
+#define TIOCA_WAR_ENABLED(pv, tioca_common) \
+       ((1 << tioca_common->ca_rev) & pv)
+
+  /* TIO:ICE:FRZ:Freezer loses a PIO data ucred on PIO RD RSP with CW error */
+#define PV907908 (1 << 1)
+  /* ATI config space problems after BIOS execution starts */
+#define PV908234 (1 << 1)
+  /* CA:AGPDMA write request data mismatch with ABC1CL merge */
+#define PV895469 (1 << 1)
+  /* TIO:CA TLB invalidate of written GART entries possibly not occuring in CA*/
+#define PV910244 (1 << 1)
+
+struct tioca_dmamap{
+       struct list_head        cad_list;       /* headed by ca_list */
+
+       dma_addr_t              cad_dma_addr;   /* Linux dma handle */
+       uint                    cad_gart_entry; /* start entry in ca_gart_pagemap */
+       uint                    cad_gart_size;  /* #entries for this map */
+};
+
+/*
+ * Kernel only fields.  Prom may look at this stuff for debugging only.
+ * Access this structure through the ca_kernel_private ptr.
+ */
+
+struct tioca_common ;
+
+struct tioca_kernel {
+       struct tioca_common     *ca_common;     /* tioca this belongs to */
+       struct list_head        ca_list;        /* list of all ca's */
+       struct list_head        ca_dmamaps;
+       spinlock_t              ca_lock;        /* Kernel lock */
+       cnodeid_t               ca_closest_node;
+       struct list_head        *ca_devices;    /* bus->devices */
+
+       /*
+        * General GART stuff
+        */
+       uint64_t        ca_ap_size;             /* size of aperature in bytes */
+       uint32_t        ca_gart_entries;        /* # uint64_t entries in gart */
+       uint32_t        ca_ap_pagesize;         /* aperature page size in bytes */
+       uint64_t        ca_ap_bus_base;         /* bus address of CA aperature */
+       uint64_t        ca_gart_size;           /* gart size in bytes */
+       uint64_t        *ca_gart;               /* gart table vaddr */
+       uint64_t        ca_gart_coretalk_addr;  /* gart coretalk addr */
+       uint8_t         ca_gart_iscoherent;     /* used in tioca_tlbflush */
+
+       /* PCI GART convenience values */
+       uint64_t        ca_pciap_base;          /* pci aperature bus base address */
+       uint64_t        ca_pciap_size;          /* pci aperature size (bytes) */
+       uint64_t        ca_pcigart_base;        /* gfx GART bus base address */
+       uint64_t        *ca_pcigart;            /* gfx GART vm address */
+       uint32_t        ca_pcigart_entries;
+       uint32_t        ca_pcigart_start;       /* PCI start index in ca_gart */
+       void            *ca_pcigart_pagemap;
+
+       /* AGP GART convenience values */
+       uint64_t        ca_gfxap_base;          /* gfx aperature bus base address */
+       uint64_t        ca_gfxap_size;          /* gfx aperature size (bytes) */
+       uint64_t        ca_gfxgart_base;        /* gfx GART bus base address */
+       uint64_t        *ca_gfxgart;            /* gfx GART vm address */
+       uint32_t        ca_gfxgart_entries;
+       uint32_t        ca_gfxgart_start;       /* agpgart start index in ca_gart */
+};
+
+/*
+ * Common tioca info shared between kernel and prom
+ *
+ * DO NOT CHANGE THIS STRUCT WITHOUT MAKING CORRESPONDING CHANGES
+ * TO THE PROM VERSION.
+ */
+
+struct tioca_common {
+       struct pcibus_bussoft   ca_common;      /* common pciio header */
+
+       uint32_t                ca_rev;
+       uint32_t                ca_closest_nasid;
+
+       uint64_t                ca_prom_private;
+       uint64_t                ca_kernel_private;
+};
+
+/**
+ * tioca_paddr_to_gart - Convert an SGI coretalk address to a CA GART entry
+ * @paddr: page address to convert
+ *
+ * Convert a system [coretalk] address to a GART entry.  GART entries are
+ * formed using the following:
+ *
+ *     data = ( (1<<63) |  ( (REMAP_NODE_ID << 40) | (MD_CHIPLET_ID << 38) | 
+ * (REMAP_SYS_ADDR) ) >> 12 )
+ *
+ * DATA written to 1 GART TABLE Entry in system memory is remapped system
+ * addr for 1 page 
+ *
+ * The data is for coretalk address format right shifted 12 bits with a
+ * valid bit.
+ *
+ *     GART_TABLE_ENTRY [ 25:0 ]  -- REMAP_SYS_ADDRESS[37:12].
+ *     GART_TABLE_ENTRY [ 27:26 ] -- SHUB MD chiplet id.
+ *     GART_TABLE_ENTRY [ 41:28 ] -- REMAP_NODE_ID.
+ *     GART_TABLE_ENTRY [ 63 ]    -- Valid Bit 
+ */
+static inline u64
+tioca_paddr_to_gart(unsigned long paddr)
+{
+       /*
+        * We are assuming right now that paddr already has the correct
+        * format since the address from xtalk_dmaXXX should already have
+        * NODE_ID, CHIPLET_ID, and SYS_ADDR in the correct locations.
+        */
+
+       return ((paddr) >> 12) | (1UL << 63);
+}
+
+/**
+ * tioca_physpage_to_gart - Map a host physical page for SGI CA based DMA
+ * @page_addr: system page address to map
+ */
+
+static inline unsigned long
+tioca_physpage_to_gart(uint64_t page_addr)
+{
+       uint64_t coretalk_addr;
+
+       coretalk_addr = PHYS_TO_TIODMA(page_addr);
+       if (!coretalk_addr) {
+               return 0;
+       }
+
+       return tioca_paddr_to_gart(coretalk_addr);
+}
+
+/**
+ * tioca_tlbflush - invalidate cached SGI CA GART TLB entries
+ * @tioca_kernel: CA context 
+ *
+ * Invalidate tlb entries for a given CA GART.  Main complexity is to account
+ * for revA bug.
+ */
+static inline void
+tioca_tlbflush(struct tioca_kernel *tioca_kernel)
+{
+       volatile uint64_t tmp;
+       volatile struct tioca *ca_base;
+       struct tioca_common *tioca_common;
+
+       tioca_common = tioca_kernel->ca_common;
+       ca_base = (struct tioca *)tioca_common->ca_common.bs_base;
+
+       /*
+        * Explicit flushes not needed if GART is in cached mode
+        */
+       if (tioca_kernel->ca_gart_iscoherent) {
+               if (TIOCA_WAR_ENABLED(PV910244, tioca_common)) {
+                       /*
+                        * PV910244:  RevA CA needs explicit flushes.
+                        * Need to put GART into uncached mode before
+                        * flushing otherwise the explicit flush is ignored.
+                        *
+                        * Alternate WAR would be to leave GART cached and
+                        * touch every CL aligned GART entry.
+                        */
+
+                       ca_base->ca_control2 &= ~(CA_GART_MEM_PARAM);
+                       ca_base->ca_control2 |= CA_GART_FLUSH_TLB;
+                       ca_base->ca_control2 |=
+                           (0x2ull << CA_GART_MEM_PARAM_SHFT);
+                       tmp = ca_base->ca_control2;
+               }
+
+               return;
+       }
+
+       /*
+        * Gart in uncached mode ... need an explicit flush.
+        */
+
+       ca_base->ca_control2 |= CA_GART_FLUSH_TLB;
+       tmp = ca_base->ca_control2;
+}
+
+extern uint32_t        tioca_gart_found;
+extern int tioca_init_provider(void);
+extern void tioca_fastwrite_enable(struct tioca_kernel *tioca_kern);
+#endif /* _ASM_IA64_SN_TIO_CA_AGP_PROVIDER_H */
diff --git a/include/asm-ia64/sn/xp.h b/include/asm-ia64/sn/xp.h
new file mode 100644 (file)
index 0000000..9902185
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ * 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-2005 Silicon Graphics, Inc. All rights reserved.
+ */
+
+
+/*
+ * External Cross Partition (XP) structures and defines.
+ */
+
+
+#ifndef _ASM_IA64_SN_XP_H
+#define _ASM_IA64_SN_XP_H
+
+
+#include <linux/version.h>
+#include <linux/cache.h>
+#include <linux/hardirq.h>
+#include <asm/sn/types.h>
+#include <asm/sn/bte.h>
+
+
+#ifdef USE_DBUG_ON
+#define DBUG_ON(condition)     BUG_ON(condition)
+#else
+#define DBUG_ON(condition)
+#endif
+
+
+/*
+ * Define the maximum number of logically defined partitions the system
+ * can support. It is constrained by the maximum number of hardware
+ * partitionable regions. The term 'region' in this context refers to the
+ * minimum number of nodes that can comprise an access protection grouping.
+ * The access protection is in regards to memory, IPI and IOI.
+ *
+ * The maximum number of hardware partitionable regions is equal to the
+ * maximum number of nodes in the entire system divided by the minimum number
+ * of nodes that comprise an access protection grouping.
+ */
+#define XP_MAX_PARTITIONS      64
+
+
+/*
+ * Define the number of u64s required to represent all the C-brick nasids
+ * as a bitmap.  The cross-partition kernel modules deal only with
+ * C-brick nasids, thus the need for bitmaps which don't account for
+ * odd-numbered (non C-brick) nasids.
+ */
+#define XP_MAX_PHYSNODE_ID     (MAX_PHYSNODE_ID / 2)
+#define XP_NASID_MASK_BYTES    ((XP_MAX_PHYSNODE_ID + 7) / 8)
+#define XP_NASID_MASK_WORDS    ((XP_MAX_PHYSNODE_ID + 63) / 64)
+
+
+/*
+ * Wrapper for bte_copy() that should it return a failure status will retry
+ * the bte_copy() once in the hope that the failure was due to a temporary
+ * aberration (i.e., the link going down temporarily).
+ *
+ * See bte_copy for definition of the input parameters.
+ *
+ * Note: xp_bte_copy() should never be called while holding a spinlock.
+ */
+static inline bte_result_t
+xp_bte_copy(u64 src, u64 dest, u64 len, u64 mode, void *notification)
+{
+       bte_result_t ret;
+
+
+       ret = bte_copy(src, dest, len, mode, notification);
+
+       if (ret != BTE_SUCCESS) {
+               if (!in_interrupt()) {
+                       cond_resched();
+               }
+               ret = bte_copy(src, dest, len, mode, notification);
+       }
+
+       return ret;
+}
+
+
+/*
+ * XPC establishes channel connections between the local partition and any
+ * other partition that is currently up. Over these channels, kernel-level
+ * `users' can communicate with their counterparts on the other partitions.
+ *
+ * The maxinum number of channels is limited to eight. For performance reasons,
+ * the internal cross partition structures require sixteen bytes per channel,
+ * and eight allows all of this interface-shared info to fit in one cache line.
+ *
+ * XPC_NCHANNELS reflects the total number of channels currently defined.
+ * If the need for additional channels arises, one can simply increase
+ * XPC_NCHANNELS accordingly. If the day should come where that number
+ * exceeds the MAXIMUM number of channels allowed (eight), then one will need
+ * to make changes to the XPC code to allow for this.
+ */
+#define XPC_MEM_CHANNEL                0       /* memory channel number */
+#define        XPC_NET_CHANNEL         1       /* network channel number */
+
+#define        XPC_NCHANNELS           2       /* #of defined channels */
+#define XPC_MAX_NCHANNELS      8       /* max #of channels allowed */
+
+#if XPC_NCHANNELS > XPC_MAX_NCHANNELS
+#error XPC_NCHANNELS exceeds MAXIMUM allowed.
+#endif
+
+
+/*
+ * The format of an XPC message is as follows:
+ *
+ *      +-------+--------------------------------+
+ *      | flags |////////////////////////////////|
+ *      +-------+--------------------------------+
+ *      |             message #                  |
+ *      +----------------------------------------+
+ *      |     payload (user-defined message)     |
+ *      |                                        |
+ *                     :
+ *      |                                        |
+ *      +----------------------------------------+
+ *
+ * The size of the payload is defined by the user via xpc_connect(). A user-
+ * defined message resides in the payload area.
+ *
+ * The user should have no dealings with the message header, but only the
+ * message's payload. When a message entry is allocated (via xpc_allocate())
+ * a pointer to the payload area is returned and not the actual beginning of
+ * the XPC message. The user then constructs a message in the payload area
+ * and passes that pointer as an argument on xpc_send() or xpc_send_notify().
+ *
+ * The size of a message entry (within a message queue) must be a cacheline
+ * sized multiple in order to facilitate the BTE transfer of messages from one
+ * message queue to another. A macro, XPC_MSG_SIZE(), is provided for the user
+ * that wants to fit as many msg entries as possible in a given memory size
+ * (e.g. a memory page).
+ */
+struct xpc_msg {
+       u8 flags;               /* FOR XPC INTERNAL USE ONLY */
+       u8 reserved[7];         /* FOR XPC INTERNAL USE ONLY */
+       s64 number;             /* FOR XPC INTERNAL USE ONLY */
+
+       u64 payload;            /* user defined portion of message */
+};
+
+
+#define XPC_MSG_PAYLOAD_OFFSET (u64) (&((struct xpc_msg *)0)->payload)
+#define XPC_MSG_SIZE(_payload_size) \
+               L1_CACHE_ALIGN(XPC_MSG_PAYLOAD_OFFSET + (_payload_size))
+
+
+/*
+ * Define the return values and values passed to user's callout functions.
+ * (It is important to add new value codes at the end just preceding
+ * xpcUnknownReason, which must have the highest numerical value.)
+ */
+enum xpc_retval {
+       xpcSuccess = 0,
+
+       xpcNotConnected,        /*  1: channel is not connected */
+       xpcConnected,           /*  2: channel connected (opened) */
+       xpcRETIRED1,            /*  3: (formerly xpcDisconnected) */
+
+       xpcMsgReceived,         /*  4: message received */
+       xpcMsgDelivered,        /*  5: message delivered and acknowledged */
+
+       xpcRETIRED2,            /*  6: (formerly xpcTransferFailed) */
+
+       xpcNoWait,              /*  7: operation would require wait */
+       xpcRetry,               /*  8: retry operation */
+       xpcTimeout,             /*  9: timeout in xpc_allocate_msg_wait() */
+       xpcInterrupted,         /* 10: interrupted wait */
+
+       xpcUnequalMsgSizes,     /* 11: message size disparity between sides */
+       xpcInvalidAddress,      /* 12: invalid address */
+
+       xpcNoMemory,            /* 13: no memory available for XPC structures */
+       xpcLackOfResources,     /* 14: insufficient resources for operation */
+       xpcUnregistered,        /* 15: channel is not registered */
+       xpcAlreadyRegistered,   /* 16: channel is already registered */
+
+       xpcPartitionDown,       /* 17: remote partition is down */
+       xpcNotLoaded,           /* 18: XPC module is not loaded */
+       xpcUnloading,           /* 19: this side is unloading XPC module */
+
+       xpcBadMagic,            /* 20: XPC MAGIC string not found */
+
+       xpcReactivating,        /* 21: remote partition was reactivated */
+
+       xpcUnregistering,       /* 22: this side is unregistering channel */
+       xpcOtherUnregistering,  /* 23: other side is unregistering channel */
+
+       xpcCloneKThread,        /* 24: cloning kernel thread */
+       xpcCloneKThreadFailed,  /* 25: cloning kernel thread failed */
+
+       xpcNoHeartbeat,         /* 26: remote partition has no heartbeat */
+
+       xpcPioReadError,        /* 27: PIO read error */
+       xpcPhysAddrRegFailed,   /* 28: registration of phys addr range failed */
+
+       xpcBteDirectoryError,   /* 29: maps to BTEFAIL_DIR */
+       xpcBtePoisonError,      /* 30: maps to BTEFAIL_POISON */
+       xpcBteWriteError,       /* 31: maps to BTEFAIL_WERR */
+       xpcBteAccessError,      /* 32: maps to BTEFAIL_ACCESS */
+       xpcBtePWriteError,      /* 33: maps to BTEFAIL_PWERR */
+       xpcBtePReadError,       /* 34: maps to BTEFAIL_PRERR */
+       xpcBteTimeOutError,     /* 35: maps to BTEFAIL_TOUT */
+       xpcBteXtalkError,       /* 36: maps to BTEFAIL_XTERR */
+       xpcBteNotAvailable,     /* 37: maps to BTEFAIL_NOTAVAIL */
+       xpcBteUnmappedError,    /* 38: unmapped BTEFAIL_ error */
+
+       xpcBadVersion,          /* 39: bad version number */
+       xpcVarsNotSet,          /* 40: the XPC variables are not set up */
+       xpcNoRsvdPageAddr,      /* 41: unable to get rsvd page's phys addr */
+       xpcInvalidPartid,       /* 42: invalid partition ID */
+       xpcLocalPartid,         /* 43: local partition ID */
+
+       xpcUnknownReason        /* 44: unknown reason -- must be last in list */
+};
+
+
+/*
+ * Define the callout function types used by XPC to update the user on
+ * connection activity and state changes (via the user function registered by
+ * xpc_connect()) and to notify them of messages received and delivered (via
+ * the user function registered by xpc_send_notify()).
+ *
+ * The two function types are xpc_channel_func and xpc_notify_func and
+ * both share the following arguments, with the exception of "data", which
+ * only xpc_channel_func has.
+ *
+ * Arguments:
+ *
+ *     reason - reason code. (See following table.)
+ *     partid - partition ID associated with condition.
+ *     ch_number - channel # associated with condition.
+ *     data - pointer to optional data. (See following table.)
+ *     key - pointer to optional user-defined value provided as the "key"
+ *           argument to xpc_connect() or xpc_send_notify().
+ *
+ * In the following table the "Optional Data" column applies to callouts made
+ * to functions registered by xpc_connect(). A "NA" in that column indicates
+ * that this reason code can be passed to functions registered by
+ * xpc_send_notify() (i.e. they don't have data arguments).
+ *
+ * Also, the first three reason codes in the following table indicate
+ * success, whereas the others indicate failure. When a failure reason code
+ * is received, one can assume that the channel is not connected.
+ *
+ *
+ * Reason Code          | Cause                          | Optional Data
+ * =====================+================================+=====================
+ * xpcConnected         | connection has been established| max #of entries
+ *                      | to the specified partition on  | allowed in message
+ *                      | the specified channel          | queue
+ * ---------------------+--------------------------------+---------------------
+ * xpcMsgReceived       | an XPC message arrived from    | address of payload
+ *                      | the specified partition on the |
+ *                      | specified channel              | [the user must call
+ *                      |                                | xpc_received() when
+ *                      |                                | finished with the
+ *                      |                                | payload]
+ * ---------------------+--------------------------------+---------------------
+ * xpcMsgDelivered      | notification that the message  | NA
+ *                      | was delivered to the intended  |
+ *                      | recipient and that they have   |
+ *                      | acknowledged its receipt by    |
+ *                      | calling xpc_received()         |
+ * =====================+================================+=====================
+ * xpcUnequalMsgSizes   | can't connect to the specified | NULL
+ *                      | partition on the specified     |
+ *                      | channel because of mismatched  |
+ *                      | message sizes                  |
+ * ---------------------+--------------------------------+---------------------
+ * xpcNoMemory          | insufficient memory avaiable   | NULL
+ *                      | to allocate message queue      |
+ * ---------------------+--------------------------------+---------------------
+ * xpcLackOfResources   | lack of resources to create    | NULL
+ *                      | the necessary kthreads to      |
+ *                      | support the channel            |
+ * ---------------------+--------------------------------+---------------------
+ * xpcUnregistering     | this side's user has           | NULL or NA
+ *                      | unregistered by calling        |
+ *                      | xpc_disconnect()               |
+ * ---------------------+--------------------------------+---------------------
+ * xpcOtherUnregistering| the other side's user has      | NULL or NA
+ *                      | unregistered by calling        |
+ *                      | xpc_disconnect()               |
+ * ---------------------+--------------------------------+---------------------
+ * xpcNoHeartbeat       | the other side's XPC is no     | NULL or NA
+ *                      | longer heartbeating            |
+ *                      |                                |
+ * ---------------------+--------------------------------+---------------------
+ * xpcUnloading         | this side's XPC module is      | NULL or NA
+ *                      | being unloaded                 |
+ *                      |                                |
+ * ---------------------+--------------------------------+---------------------
+ * xpcOtherUnloading    | the other side's XPC module is | NULL or NA
+ *                      | is being unloaded              |
+ *                      |                                |
+ * ---------------------+--------------------------------+---------------------
+ * xpcPioReadError      | xp_nofault_PIOR() returned an  | NULL or NA
+ *                      | error while sending an IPI     |
+ *                      |                                |
+ * ---------------------+--------------------------------+---------------------
+ * xpcInvalidAddress    | the address either received or | NULL or NA
+ *                      | sent by the specified partition|
+ *                      | is invalid                     |
+ * ---------------------+--------------------------------+---------------------
+ * xpcBteNotAvailable   | attempt to pull data from the  | NULL or NA
+ * xpcBtePoisonError    | specified partition over the   |
+ * xpcBteWriteError     | specified channel via a        |
+ * xpcBteAccessError    | bte_copy() failed              |
+ * xpcBteTimeOutError   |                                |
+ * xpcBteXtalkError     |                                |
+ * xpcBteDirectoryError |                                |
+ * xpcBteGenericError   |                                |
+ * xpcBteUnmappedError  |                                |
+ * ---------------------+--------------------------------+---------------------
+ * xpcUnknownReason     | the specified channel to the   | NULL or NA
+ *                      | specified partition was        |
+ *                      | unavailable for unknown reasons|
+ * =====================+================================+=====================
+ */
+
+typedef void (*xpc_channel_func)(enum xpc_retval reason, partid_t partid,
+               int ch_number, void *data, void *key);
+
+typedef void (*xpc_notify_func)(enum xpc_retval reason, partid_t partid,
+               int ch_number, void *key);
+
+
+/*
+ * The following is a registration entry. There is a global array of these,
+ * one per channel. It is used to record the connection registration made
+ * by the users of XPC. As long as a registration entry exists, for any
+ * partition that comes up, XPC will attempt to establish a connection on
+ * that channel. Notification that a connection has been made will occur via
+ * the xpc_channel_func function.
+ *
+ * The 'func' field points to the function to call when aynchronous
+ * notification is required for such events as: a connection established/lost,
+ * or an incomming message received, or an error condition encountered. A
+ * non-NULL 'func' field indicates that there is an active registration for
+ * the channel.
+ */
+struct xpc_registration {
+       struct semaphore sema;
+       xpc_channel_func func;          /* function to call */
+       void *key;                      /* pointer to user's key */
+       u16 nentries;                   /* #of msg entries in local msg queue */
+       u16 msg_size;                   /* message queue's message size */
+       u32 assigned_limit;             /* limit on #of assigned kthreads */
+       u32 idle_limit;                 /* limit on #of idle kthreads */
+} ____cacheline_aligned;
+
+
+#define XPC_CHANNEL_REGISTERED(_c)     (xpc_registrations[_c].func != NULL)
+
+
+/* the following are valid xpc_allocate() flags */
+#define XPC_WAIT       0               /* wait flag */
+#define XPC_NOWAIT     1               /* no wait flag */
+
+
+struct xpc_interface {
+       void (*connect)(int);
+       void (*disconnect)(int);
+       enum xpc_retval (*allocate)(partid_t, int, u32, void **);
+       enum xpc_retval (*send)(partid_t, int, void *);
+       enum xpc_retval (*send_notify)(partid_t, int, void *,
+                                               xpc_notify_func, void *);
+       void (*received)(partid_t, int, void *);
+       enum xpc_retval (*partid_to_nasids)(partid_t, void *);
+};
+
+
+extern struct xpc_interface xpc_interface;
+
+extern void xpc_set_interface(void (*)(int),
+               void (*)(int),
+               enum xpc_retval (*)(partid_t, int, u32, void **),
+               enum xpc_retval (*)(partid_t, int, void *),
+               enum xpc_retval (*)(partid_t, int, void *, xpc_notify_func,
+                                                               void *),
+               void (*)(partid_t, int, void *),
+               enum xpc_retval (*)(partid_t, void *));
+extern void xpc_clear_interface(void);
+
+
+extern enum xpc_retval xpc_connect(int, xpc_channel_func, void *, u16,
+                                               u16, u32, u32);
+extern void xpc_disconnect(int);
+
+static inline enum xpc_retval
+xpc_allocate(partid_t partid, int ch_number, u32 flags, void **payload)
+{
+       return xpc_interface.allocate(partid, ch_number, flags, payload);
+}
+
+static inline enum xpc_retval
+xpc_send(partid_t partid, int ch_number, void *payload)
+{
+       return xpc_interface.send(partid, ch_number, payload);
+}
+
+static inline enum xpc_retval
+xpc_send_notify(partid_t partid, int ch_number, void *payload,
+                       xpc_notify_func func, void *key)
+{
+       return xpc_interface.send_notify(partid, ch_number, payload, func, key);
+}
+
+static inline void
+xpc_received(partid_t partid, int ch_number, void *payload)
+{
+       return xpc_interface.received(partid, ch_number, payload);
+}
+
+static inline enum xpc_retval
+xpc_partid_to_nasids(partid_t partid, void *nasids)
+{
+       return xpc_interface.partid_to_nasids(partid, nasids);
+}
+
+
+extern u64 xp_nofault_PIOR_target;
+extern int xp_nofault_PIOR(void *);
+extern int xp_error_PIOR(void);
+
+
+#endif /* _ASM_IA64_SN_XP_H */
+
diff --git a/include/asm-ppc/ipic.h b/include/asm-ppc/ipic.h
new file mode 100644 (file)
index 0000000..9092b92
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * include/asm-ppc/ipic.h
+ *
+ * IPIC external definitions and structure.
+ *
+ * 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_IPIC_H__
+#define __ASM_IPIC_H__
+
+#include <linux/irq.h>
+
+/* Flags when we init the IPIC */
+#define IPIC_SPREADMODE_GRP_A  0x00000001
+#define IPIC_SPREADMODE_GRP_D  0x00000002
+#define IPIC_SPREADMODE_MIX_A  0x00000004
+#define IPIC_SPREADMODE_MIX_B  0x00000008
+#define IPIC_DISABLE_MCP_OUT   0x00000010
+#define IPIC_IRQ0_MCP          0x00000020
+
+/* IPIC registers offsets */
+#define IPIC_SICFR     0x00    /* System Global Interrupt Configuration Register */
+#define IPIC_SIVCR     0x04    /* System Global Interrupt Vector Register */
+#define IPIC_SIPNR_H   0x08    /* System Internal Interrupt Pending Register (HIGH) */
+#define IPIC_SIPNR_L   0x0C    /* System Internal Interrupt Pending Register (LOW) */
+#define IPIC_SIPRR_A   0x10    /* System Internal Interrupt group A Priority Register */
+#define IPIC_SIPRR_B   0x14    /* System Internal Interrupt group B Priority Register */
+#define IPIC_SIPRR_C   0x18    /* System Internal Interrupt group C Priority Register */
+#define IPIC_SIPRR_D   0x1C    /* System Internal Interrupt group D Priority Register */
+#define IPIC_SIMSR_H   0x20    /* System Internal Interrupt Mask Register (HIGH) */
+#define IPIC_SIMSR_L   0x24    /* System Internal Interrupt Mask Register (LOW) */
+#define IPIC_SICNR     0x28    /* System Internal Interrupt Control Register */
+#define IPIC_SEPNR     0x2C    /* System External Interrupt Pending Register */
+#define IPIC_SMPRR_A   0x30    /* System Mixed Interrupt group A Priority Register */
+#define IPIC_SMPRR_B   0x34    /* System Mixed Interrupt group B Priority Register */
+#define IPIC_SEMSR     0x38    /* System External Interrupt Mask Register */
+#define IPIC_SECNR     0x3C    /* System External Interrupt Control Register */
+#define IPIC_SERSR     0x40    /* System Error Status Register */
+#define IPIC_SERMR     0x44    /* System Error Mask Register */
+#define IPIC_SERCR     0x48    /* System Error Control Register */
+#define IPIC_SIFCR_H   0x50    /* System Internal Interrupt Force Register (HIGH) */
+#define IPIC_SIFCR_L   0x54    /* System Internal Interrupt Force Register (LOW) */
+#define IPIC_SEFCR     0x58    /* System External Interrupt Force Register */
+#define IPIC_SERFR     0x5C    /* System Error Force Register */
+#define IPIC_SCVCR     0x60    /* System Critical Interrupt Vector Register */
+#define IPIC_SMVCR     0x64    /* System Management Interrupt Vector Register */
+
+enum ipic_prio_grp {
+       IPIC_INT_GRP_A = IPIC_SIPRR_A,
+       IPIC_INT_GRP_D = IPIC_SIPRR_D,
+       IPIC_MIX_GRP_A = IPIC_SMPRR_A,
+       IPIC_MIX_GRP_B = IPIC_SMPRR_B,
+};
+
+enum ipic_mcp_irq {
+       IPIC_MCP_IRQ0 = 0,
+       IPIC_MCP_WDT  = 1,
+       IPIC_MCP_SBA  = 2,
+       IPIC_MCP_PCI1 = 5,
+       IPIC_MCP_PCI2 = 6,
+       IPIC_MCP_MU   = 7,
+};
+
+extern void ipic_init(phys_addr_t phys_addr, unsigned int flags,
+               unsigned int irq_offset,
+               unsigned char *senses, unsigned int senses_count);
+extern int ipic_set_priority(unsigned int irq, unsigned int priority);
+extern void ipic_set_highest_priority(unsigned int irq);
+extern void ipic_set_default_priority(void);
+extern void ipic_enable_mcp(enum ipic_mcp_irq mcp_irq);
+extern void ipic_disable_mcp(enum ipic_mcp_irq mcp_irq);
+extern u32 ipic_get_mcp_status(void);
+extern void ipic_clear_mcp_status(u32 mask);
+extern int ipic_get_irq(struct pt_regs *regs);
+
+#endif /* __ASM_IPIC_H__ */
+#endif /* __KERNEL__ */
diff --git a/include/asm-ppc/mpc83xx.h b/include/asm-ppc/mpc83xx.h
new file mode 100644 (file)
index 0000000..bb1b057
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * include/asm-ppc/mpc83xx.h
+ *
+ * MPC83xx definitions
+ *
+ * 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_MPC83xx_H__
+#define __ASM_MPC83xx_H__
+
+#include <linux/config.h>
+#include <asm/mmu.h>
+
+#ifdef CONFIG_83xx
+
+#ifdef CONFIG_MPC834x_SYS
+#include <platforms/83xx/mpc834x_sys.h>
+#endif
+
+#define _IO_BASE        isa_io_base
+#define _ISA_MEM_BASE   isa_mem_base
+#ifdef CONFIG_PCI
+#define PCI_DRAM_OFFSET pci_dram_offset
+#else
+#define PCI_DRAM_OFFSET 0
+#endif
+
+/*
+ * The "residual" board information structure the boot loader passes
+ * into the kernel.
+ */
+extern unsigned char __res[];
+
+/* Internal IRQs on MPC83xx OpenPIC */
+/* Not all of these exist on all MPC83xx implementations */
+
+#ifndef MPC83xx_IPIC_IRQ_OFFSET
+#define MPC83xx_IPIC_IRQ_OFFSET        0
+#endif
+
+#define NR_IPIC_INTS 128
+
+#define MPC83xx_IRQ_UART1      ( 9 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_UART2      (10 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_SEC2       (11 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_IIC1       (14 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_IIC2       (15 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_SPI                (16 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_EXT1       (17 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_EXT2       (18 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_EXT3       (19 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_EXT4       (20 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_EXT5       (21 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_EXT6       (22 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_EXT7       (23 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_TSEC1_TX   (32 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_TSEC1_RX   (33 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_TSEC1_ERROR        (34 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_TSEC2_TX   (35 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_TSEC2_RX   (36 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_TSEC2_ERROR        (37 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_USB2_DR    (38 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_USB2_MPH   (39 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_EXT0       (48 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_RTC_SEC    (64 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_PIT                (65 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_PCI1       (66 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_PCI2       (67 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_RTC_ALR    (68 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_MU         (69 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_SBA                (70 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_DMA                (71 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_GTM4       (72 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_GTM8       (73 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_GPIO1      (74 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_GPIO2      (75 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_DDR                (76 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_LBC                (77 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_GTM2       (78 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_GTM6       (79 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_PMC                (80 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_GTM3       (84 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_GTM7       (85 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_GTM1       (90 + MPC83xx_IPIC_IRQ_OFFSET)
+#define MPC83xx_IRQ_GTM5       (91 + MPC83xx_IPIC_IRQ_OFFSET)
+
+#define MPC83xx_CCSRBAR_SIZE   (1024*1024)
+
+/* Let modules/drivers get at immrbar (physical) */
+extern phys_addr_t immrbar;
+
+enum ppc_sys_devices {
+       MPC83xx_TSEC1,
+       MPC83xx_TSEC2,
+       MPC83xx_IIC1,
+       MPC83xx_IIC2,
+       MPC83xx_DUART,
+       MPC83xx_SEC2,
+       MPC83xx_USB2_DR,
+       MPC83xx_USB2_MPH,
+};
+
+#endif /* CONFIG_83xx */
+#endif /* __ASM_MPC83xx_H__ */
+#endif /* __KERNEL__ */
diff --git a/include/asm-ppc64/agp.h b/include/asm-ppc64/agp.h
new file mode 100644 (file)
index 0000000..ca9e423
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef AGP_H
+#define AGP_H 1
+
+#include <asm/io.h>
+
+/* nothing much needed here */
+
+#define map_page_into_agp(page)
+#define unmap_page_from_agp(page)
+#define flush_agp_mappings()
+#define flush_agp_cache() mb()
+
+/* Convert a physical address to an address suitable for the GART. */
+#define phys_to_gart(x) (x)
+#define gart_to_phys(x) (x)
+
+/* GATT allocation. Returns/accepts GATT kernel virtual address. */
+#define alloc_gatt_pages(order)                \
+       ((char *)__get_free_pages(GFP_KERNEL, (order)))
+#define free_gatt_pages(table, order)  \
+       free_pages((unsigned long)(table), (order))
+
+#endif
diff --git a/include/asm-ppc64/imalloc.h b/include/asm-ppc64/imalloc.h
new file mode 100644 (file)
index 0000000..3a45e91
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef _PPC64_IMALLOC_H
+#define _PPC64_IMALLOC_H
+
+/*
+ * Define the address range of the imalloc VM area.
+ */
+#define PHBS_IO_BASE     IOREGIONBASE
+#define IMALLOC_BASE      (IOREGIONBASE + 0x80000000ul)        /* Reserve 2 gigs for PHBs */
+#define IMALLOC_END       (IOREGIONBASE + EADDR_MASK)
+
+
+/* imalloc region types */
+#define IM_REGION_UNUSED       0x1
+#define IM_REGION_SUBSET       0x2
+#define IM_REGION_EXISTS       0x4
+#define IM_REGION_OVERLAP      0x8
+#define IM_REGION_SUPERSET     0x10
+
+extern struct vm_struct * im_get_free_area(unsigned long size);
+extern struct vm_struct * im_get_area(unsigned long v_addr, unsigned long size,
+                       int region_type);
+unsigned long im_free(void *addr);
+
+#endif /* _PPC64_IMALLOC_H */
diff --git a/include/asm-ppc64/pSeries_reconfig.h b/include/asm-ppc64/pSeries_reconfig.h
new file mode 100644 (file)
index 0000000..c0db1ea
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef _PPC64_PSERIES_RECONFIG_H
+#define _PPC64_PSERIES_RECONFIG_H
+
+#include <linux/notifier.h>
+
+/*
+ * Use this API if your code needs to know about OF device nodes being
+ * added or removed on pSeries systems.
+ */
+
+#define PSERIES_RECONFIG_ADD    0x0001
+#define PSERIES_RECONFIG_REMOVE 0x0002
+
+#ifdef CONFIG_PPC_PSERIES
+extern int pSeries_reconfig_notifier_register(struct notifier_block *);
+extern void pSeries_reconfig_notifier_unregister(struct notifier_block *);
+#else /* !CONFIG_PPC_PSERIES */
+static inline int pSeries_reconfig_notifier_register(struct notifier_block *nb)
+{
+       return 0;
+}
+static inline void pSeries_reconfig_notifier_unregister(struct notifier_block *nb) { }
+#endif /* CONFIG_PPC_PSERIES */
+
+#endif /* _PPC64_PSERIES_RECONFIG_H */
diff --git a/include/asm-ppc64/pmc.h b/include/asm-ppc64/pmc.h
new file mode 100644 (file)
index 0000000..c924748
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * pmc.h
+ * Copyright (C) 2004  David Gibson, 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 _PPC64_PMC_H
+#define _PPC64_PMC_H
+
+#include <asm/ptrace.h>
+
+typedef void (*perf_irq_t)(struct pt_regs *);
+
+int reserve_pmc_hardware(perf_irq_t new_perf_irq);
+void release_pmc_hardware(void);
+
+#endif /* _PPC64_PMC_H */
diff --git a/include/asm-ppc64/seccomp.h b/include/asm-ppc64/seccomp.h
new file mode 100644 (file)
index 0000000..c130c33
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef _ASM_SECCOMP_H
+
+#include <linux/thread_info.h> /* already defines TIF_32BIT */
+
+#ifndef TIF_32BIT
+#error "unexpected TIF_32BIT on ppc64"
+#endif
+
+#include <linux/unistd.h>
+
+#define __NR_seccomp_read __NR_read
+#define __NR_seccomp_write __NR_write
+#define __NR_seccomp_exit __NR_exit
+#define __NR_seccomp_sigreturn __NR_rt_sigreturn
+
+#define __NR_seccomp_read_32 __NR_read
+#define __NR_seccomp_write_32 __NR_write
+#define __NR_seccomp_exit_32 __NR_exit
+#define __NR_seccomp_sigreturn_32 __NR_sigreturn
+
+#endif /* _ASM_SECCOMP_H */
diff --git a/include/asm-ppc64/smu.h b/include/asm-ppc64/smu.h
new file mode 100644 (file)
index 0000000..10b4397
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Definitions for talking to the SMU chip in newer G5 PowerMacs
+ */
+
+#include <linux/config.h>
+
+/*
+ * Basic routines for use by architecture. To be extended as
+ * we understand more of the chip
+ */
+extern int smu_init(void);
+extern int smu_present(void);
+extern void smu_shutdown(void);
+extern void smu_restart(void);
+extern int smu_get_rtc_time(struct rtc_time *time);
+extern int smu_set_rtc_time(struct rtc_time *time);
+
+/*
+ * SMU command buffer absolute address, exported by pmac_setup,
+ * this is allocated very early during boot.
+ */
+extern unsigned long smu_cmdbuf_abs;
diff --git a/include/asm-ppc64/vdso.h b/include/asm-ppc64/vdso.h
new file mode 100644 (file)
index 0000000..85d8a7b
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef __PPC64_VDSO_H__
+#define __PPC64_VDSO_H__
+
+#ifdef __KERNEL__
+
+/* Default link addresses for the vDSOs */
+#define VDSO32_LBASE   0x100000
+#define VDSO64_LBASE   0x100000
+
+/* Default map addresses */
+#define VDSO32_MBASE   VDSO32_LBASE
+#define VDSO64_MBASE   VDSO64_LBASE
+
+#define VDSO_VERSION_STRING    LINUX_2.6.12
+
+/* Define if 64 bits VDSO has procedure descriptors */
+#undef VDS64_HAS_DESCRIPTORS
+
+#ifndef __ASSEMBLY__
+
+extern unsigned int vdso64_pages;
+extern unsigned int vdso32_pages;
+
+/* Offsets relative to thread->vdso_base */
+extern unsigned long vdso64_rt_sigtramp;
+extern unsigned long vdso32_sigtramp;
+extern unsigned long vdso32_rt_sigtramp;
+
+extern void vdso_init(void);
+
+#else /* __ASSEMBLY__ */
+
+#ifdef __VDSO64__
+#ifdef VDS64_HAS_DESCRIPTORS
+#define V_FUNCTION_BEGIN(name)         \
+       .globl name;                    \
+        .section ".opd","a";           \
+        .align 3;                      \
+       name:                           \
+       .quad .name,.TOC.@tocbase,0;    \
+       .previous;                      \
+       .globl .name;                   \
+       .type .name,@function;          \
+       .name:                          \
+
+#define V_FUNCTION_END(name)           \
+       .size .name,.-.name;
+
+#define V_LOCAL_FUNC(name) (.name)
+
+#else /* VDS64_HAS_DESCRIPTORS */
+
+#define V_FUNCTION_BEGIN(name)         \
+       .globl name;                    \
+       name:                           \
+
+#define V_FUNCTION_END(name)           \
+       .size name,.-name;
+
+#define V_LOCAL_FUNC(name) (name)
+
+#endif /* VDS64_HAS_DESCRIPTORS */
+#endif /* __VDSO64__ */
+
+#ifdef __VDSO32__
+
+#define V_FUNCTION_BEGIN(name)         \
+       .globl name;                    \
+       .type name,@function;           \
+       name:                           \
+
+#define V_FUNCTION_END(name)           \
+       .size name,.-name;
+
+#define V_LOCAL_FUNC(name) (name)
+
+#endif /* __VDSO32__ */
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __KERNEL__ */
+
+#endif /* __PPC64_VDSO_H__ */
diff --git a/include/asm-sh/cpu-sh3/timer.h b/include/asm-sh/cpu-sh3/timer.h
new file mode 100644 (file)
index 0000000..3d8e95e
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * include/asm-sh/cpu-sh3/timer.h
+ *
+ * Copyright (C) 2004 Lineo Solutions, Inc.
+ *
+ * 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_CPU_SH3_TIMER_H
+#define __ASM_CPU_SH3_TIMER_H
+
+/*
+ * ---------------------------------------------------------------------------
+ * TMU Common definitions for SH3 processors
+ *     SH7706
+ *     SH7709S
+ *     SH7727
+ *     SH7729R
+ *     SH7710
+ *     SH7720
+ *     SH7300
+ * ---------------------------------------------------------------------------
+ */
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7710)
+#define TMU_TSTR       0xa412fe92      /* Byte access */
+
+#define TMU0_TCOR      0xa412fe94      /* Long access */
+#define TMU0_TCNT      0xa412fe98      /* Long access */
+#define TMU0_TCR       0xa412fe9c      /* Word access */
+
+#define TMU1_TCOR      0xa412fea0      /* Long access */
+#define TMU1_TCNT      0xa412fea4      /* Long access */
+#define TMU1_TCR       0xa412fea8      /* Word access */
+
+#define TMU2_TCOR      0xa412feac      /* Long access */
+#define TMU2_TCNT      0xa412feb0      /* Long access */
+#define TMU2_TCR       0xa412feb4      /* Word access */
+
+#else
+#if !defined(CONFIG_CPU_SUBTYPE_SH7727)
+#define TMU_TOCR       0xfffffe90      /* Byte access */
+#endif
+#define TMU_TSTR       0xfffffe92      /* Byte access */
+
+#define TMU0_TCOR      0xfffffe94      /* Long access */
+#define TMU0_TCNT      0xfffffe98      /* Long access */
+#define TMU0_TCR       0xfffffe9c      /* Word access */
+
+#define TMU1_TCOR      0xfffffea0      /* Long access */
+#define TMU1_TCNT      0xfffffea4      /* Long access */
+#define TMU1_TCR       0xfffffea8      /* Word access */
+
+#define TMU2_TCOR      0xfffffeac      /* Long access */
+#define TMU2_TCNT      0xfffffeb0      /* Long access */
+#define TMU2_TCR       0xfffffeb4      /* Word access */
+#if !defined(CONFIG_CPU_SUBTYPE_SH7727)
+#define TMU2_TCPR2     0xfffffeb8      /* Long access */
+#endif
+#endif
+
+#endif /* __ASM_CPU_SH3_TIMER_H */
+
diff --git a/include/asm-sh/sh03/ide.h b/include/asm-sh/sh03/ide.h
new file mode 100644 (file)
index 0000000..73ee92e
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef __ASM_SH_SH03_IDE_H
+#define __ASM_SH_SH03_IDE_H
+
+#define IRQ_CFCARD     8
+#define IRQ_PCMCIA     8
+
+#endif /* __ASM_SH_SH03_IDE_H */
diff --git a/include/asm-um/elf-ppc.h b/include/asm-um/elf-ppc.h
new file mode 100644 (file)
index 0000000..2998cf9
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef __UM_ELF_PPC_H
+#define __UM_ELF_PPC_H
+
+#include "linux/config.h"
+
+extern long elf_aux_hwcap;
+#define ELF_HWCAP (elf_aux_hwcap)
+
+#define SET_PERSONALITY(ex, ibcs2) do ; while(0)
+
+#define ELF_EXEC_PAGESIZE 4096
+
+#define elf_check_arch(x) (1)
+
+#ifdef CONFIG_64_BIT
+#define ELF_CLASS ELFCLASS64
+#else
+#define ELF_CLASS ELFCLASS32
+#endif
+
+#define USE_ELF_CORE_DUMP
+
+#define R_386_NONE     0
+#define R_386_32       1
+#define R_386_PC32     2
+#define R_386_GOT32    3
+#define R_386_PLT32    4
+#define R_386_COPY     5
+#define R_386_GLOB_DAT 6
+#define R_386_JMP_SLOT 7
+#define R_386_RELATIVE 8
+#define R_386_GOTOFF   9
+#define R_386_GOTPC    10
+#define R_386_NUM      11
+
+#define ELF_PLATFORM (0)
+
+#define ELF_ET_DYN_BASE (0x08000000)
+
+/* the following stolen from asm-ppc/elf.h */
+#define ELF_NGREG      48      /* includes nip, msr, lr, etc. */
+#define ELF_NFPREG     33      /* includes fpscr */
+/* General registers */
+typedef unsigned long elf_greg_t;
+typedef elf_greg_t elf_gregset_t[ELF_NGREG];
+
+/* Floating point registers */
+typedef double elf_fpreg_t;
+typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
+
+#define ELF_DATA        ELFDATA2MSB
+#define ELF_ARCH       EM_PPC
+
+#endif
diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h
new file mode 100644 (file)
index 0000000..3438233
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef _LINUX_CPUSET_H
+#define _LINUX_CPUSET_H
+/*
+ *  cpuset interface
+ *
+ *  Copyright (C) 2003 BULL SA
+ *  Copyright (C) 2004 Silicon Graphics, Inc.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/cpumask.h>
+#include <linux/nodemask.h>
+
+#ifdef CONFIG_CPUSETS
+
+extern int cpuset_init(void);
+extern void cpuset_init_smp(void);
+extern void cpuset_fork(struct task_struct *p);
+extern void cpuset_exit(struct task_struct *p);
+extern cpumask_t cpuset_cpus_allowed(const struct task_struct *p);
+void cpuset_init_current_mems_allowed(void);
+void cpuset_update_current_mems_allowed(void);
+void cpuset_restrict_to_mems_allowed(unsigned long *nodes);
+int cpuset_zonelist_valid_mems_allowed(struct zonelist *zl);
+int cpuset_zone_allowed(struct zone *z);
+extern struct file_operations proc_cpuset_operations;
+extern char *cpuset_task_status_allowed(struct task_struct *task, char *buffer);
+
+#else /* !CONFIG_CPUSETS */
+
+static inline int cpuset_init(void) { return 0; }
+static inline void cpuset_init_smp(void) {}
+static inline void cpuset_fork(struct task_struct *p) {}
+static inline void cpuset_exit(struct task_struct *p) {}
+
+static inline cpumask_t cpuset_cpus_allowed(struct task_struct *p)
+{
+       return cpu_possible_map;
+}
+
+static inline void cpuset_init_current_mems_allowed(void) {}
+static inline void cpuset_update_current_mems_allowed(void) {}
+static inline void cpuset_restrict_to_mems_allowed(unsigned long *nodes) {}
+
+static inline int cpuset_zonelist_valid_mems_allowed(struct zonelist *zl)
+{
+       return 1;
+}
+
+static inline int cpuset_zone_allowed(struct zone *z)
+{
+       return 1;
+}
+
+static inline char *cpuset_task_status_allowed(struct task_struct *task,
+                                                       char *buffer)
+{
+       return buffer;
+}
+
+#endif /* !CONFIG_CPUSETS */
+
+#endif /* _LINUX_CPUSET_H */
diff --git a/include/linux/ioc4_common.h b/include/linux/ioc4_common.h
new file mode 100644 (file)
index 0000000..b03bcc4
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+#ifndef _LINUX_IOC4_COMMON_H
+#define _LINUX_IOC4_COMMON_H
+
+/* prototypes */
+
+int ioc4_serial_init(void);
+
+int ioc4_serial_attach_one(struct pci_dev *pdev, const struct
+                               pci_device_id *pci_id);
+int ioc4_ide_attach_one(struct pci_dev *pdev, const struct
+                               pci_device_id *pci_id);
+
+#endif /* _LINUX_IOC4_COMMON_H */
diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h
new file mode 100644 (file)
index 0000000..3a2702b
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef _LINUX_SECCOMP_H
+#define _LINUX_SECCOMP_H
+
+#include <linux/config.h>
+
+#ifdef CONFIG_SECCOMP
+
+#define NR_SECCOMP_MODES 1
+
+#include <linux/thread_info.h>
+#include <asm/seccomp.h>
+
+typedef struct { int mode; } seccomp_t;
+
+extern void __secure_computing(int);
+static inline void secure_computing(int this_syscall)
+{
+       if (unlikely(test_thread_flag(TIF_SECCOMP)))
+               __secure_computing(this_syscall);
+}
+
+#else /* CONFIG_SECCOMP */
+
+#if (__GNUC__ > 2)
+  typedef struct { } seccomp_t;
+#else
+  typedef struct { int gcc_is_buggy; } seccomp_t;
+#endif
+
+#define secure_computing(x) do { } while (0)
+
+#endif /* CONFIG_SECCOMP */
+
+#endif /* _LINUX_SECCOMP_H */
diff --git a/include/linux/usb_cdc.h b/include/linux/usb_cdc.h
new file mode 100644 (file)
index 0000000..f22d6be
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * USB Communications Device Class (CDC) definitions
+ *
+ * CDC says how to talk to lots of different types of network adapters,
+ * notably ethernet adapters and various modems.  It's used mostly with
+ * firmware based USB peripherals.
+ */
+
+#define USB_CDC_SUBCLASS_ACM                   0x02
+#define USB_CDC_SUBCLASS_ETHERNET              0x06
+#define USB_CDC_SUBCLASS_WHCM                  0x08
+#define USB_CDC_SUBCLASS_DMM                   0x09
+#define USB_CDC_SUBCLASS_MDLM                  0x0a
+#define USB_CDC_SUBCLASS_OBEX                  0x0b
+
+#define USB_CDC_PROTO_NONE                     0
+
+#define USB_CDC_ACM_PROTO_AT_V25TER            1
+#define USB_CDC_ACM_PROTO_AT_PCCA101           2
+#define USB_CDC_ACM_PROTO_AT_PCCA101_WAKE      3
+#define USB_CDC_ACM_PROTO_AT_GSM               4
+#define USB_CDC_ACM_PROTO_AT_3G                        5
+#define USB_CDC_ACM_PROTO_AT_CDMA              6
+#define USB_CDC_ACM_PROTO_VENDOR               0xff
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Class-Specific descriptors ... there are a couple dozen of them
+ */
+
+#define USB_CDC_HEADER_TYPE            0x00            /* header_desc */
+#define USB_CDC_CALL_MANAGEMENT_TYPE   0x01            /* call_mgmt_descriptor */
+#define USB_CDC_ACM_TYPE               0x02            /* acm_descriptor */
+#define USB_CDC_UNION_TYPE             0x06            /* union_desc */
+#define USB_CDC_COUNTRY_TYPE           0x07
+#define USB_CDC_ETHERNET_TYPE          0x0f            /* ether_desc */
+#define USB_CDC_WHCM_TYPE              0x11
+#define USB_CDC_MDLM_TYPE              0x12            /* mdlm_desc */
+#define USB_CDC_MDLM_DETAIL_TYPE       0x13            /* mdlm_detail_desc */
+#define USB_CDC_DMM_TYPE               0x14
+#define USB_CDC_OBEX_TYPE              0x15
+
+/* "Header Functional Descriptor" from CDC spec  5.2.3.1 */
+struct usb_cdc_header_desc {
+       __u8    bLength;
+       __u8    bDescriptorType;
+       __u8    bDescriptorSubType;
+
+       __le16  bcdCDC;
+} __attribute__ ((packed));
+
+/* "Call Management Descriptor" from CDC spec  5.2.3.2 */
+struct usb_cdc_call_mgmt_descriptor {
+       __u8    bLength;
+       __u8    bDescriptorType;
+       __u8    bDescriptorSubType;
+
+       __u8    bmCapabilities;
+#define USB_CDC_CALL_MGMT_CAP_CALL_MGMT                0x01
+#define USB_CDC_CALL_MGMT_CAP_DATA_INTF                0x02
+
+       __u8    bDataInterface;
+} __attribute__ ((packed));
+
+/* "Abstract Control Management Descriptor" from CDC spec  5.2.3.3 */
+struct usb_cdc_acm_descriptor {
+       __u8    bLength;
+       __u8    bDescriptorType;
+       __u8    bDescriptorSubType;
+
+       __u8    bmCapabilities;
+} __attribute__ ((packed));
+
+/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */
+struct usb_cdc_union_desc {
+       __u8    bLength;
+       __u8    bDescriptorType;
+       __u8    bDescriptorSubType;
+
+       __u8    bMasterInterface0;
+       __u8    bSlaveInterface0;
+       /* ... and there could be other slave interfaces */
+} __attribute__ ((packed));
+
+/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */
+struct usb_cdc_ether_desc {
+       __u8    bLength;
+       __u8    bDescriptorType;
+       __u8    bDescriptorSubType;
+
+       __u8    iMACAddress;
+       __le32  bmEthernetStatistics;
+       __le16  wMaxSegmentSize;
+       __le16  wNumberMCFilters;
+       __u8    bNumberPowerFilters;
+} __attribute__ ((packed));
+
+/* "MDLM Functional Descriptor" from CDC WMC spec 6.7.2.3 */
+struct usb_cdc_mdlm_desc {
+       __u8    bLength;
+       __u8    bDescriptorType;
+       __u8    bDescriptorSubType;
+
+       __le16  bcdVersion;
+       __u8    bGUID[16];
+} __attribute__ ((packed));
+
+/* "MDLM Detail Functional Descriptor" from CDC WMC spec 6.7.2.4 */
+struct usb_cdc_mdlm_detail_desc {
+       __u8    bLength;
+       __u8    bDescriptorType;
+       __u8    bDescriptorSubType;
+
+       /* type is associated with mdlm_desc.bGUID */
+       __u8    bGuidDescriptorType;
+       __u8    bDetailData[0];
+} __attribute__ ((packed));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Class-Specific Control Requests (6.2)
+ *
+ * section 3.6.2.1 table 4 has the ACM profile, for modems.
+ * section 3.8.2 table 10 has the ethernet profile.
+ *
+ * Microsoft's RNDIS stack for Ethernet is a vendor-specific CDC ACM variant,
+ * heavily dependent on the encapsulated (proprietary) command mechanism.
+ */
+
+#define USB_CDC_SEND_ENCAPSULATED_COMMAND      0x00
+#define USB_CDC_GET_ENCAPSULATED_RESPONSE      0x01
+#define USB_CDC_REQ_SET_LINE_CODING            0x20
+#define USB_CDC_REQ_GET_LINE_CODING            0x21
+#define USB_CDC_REQ_SET_CONTROL_LINE_STATE     0x22
+#define USB_CDC_REQ_SEND_BREAK                 0x23
+#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40
+#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41
+#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42
+#define USB_CDC_SET_ETHERNET_PACKET_FILTER     0x43
+#define USB_CDC_GET_ETHERNET_STATISTIC         0x44
+
+/* Line Coding Structure from CDC spec 6.2.13 */
+struct usb_cdc_line_coding {
+       __le32  dwDTERate;
+       __u8    bCharFormat;
+#define USB_CDC_1_STOP_BITS                    0
+#define USB_CDC_1_5_STOP_BITS                  1
+#define USB_CDC_2_STOP_BITS                    2
+
+       __u8    bParityType;
+#define USB_CDC_NO_PARITY                      0
+#define USB_CDC_ODD_PARITY                     1
+#define USB_CDC_EVEN_PARITY                    2
+#define USB_CDC_MARK_PARITY                    3
+#define USB_CDC_SPACE_PARITY                   4
+
+       __u8    bDataBits;
+} __attribute__ ((packed));
+
+/* table 62; bits in multicast filter */
+#define        USB_CDC_PACKET_TYPE_PROMISCUOUS         (1 << 0)
+#define        USB_CDC_PACKET_TYPE_ALL_MULTICAST       (1 << 1) /* no filter */
+#define        USB_CDC_PACKET_TYPE_DIRECTED            (1 << 2)
+#define        USB_CDC_PACKET_TYPE_BROADCAST           (1 << 3)
+#define        USB_CDC_PACKET_TYPE_MULTICAST           (1 << 4) /* filtered */
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Class-Specific Notifications (6.3) sent by interrupt transfers
+ *
+ * section 3.8.2 table 11 of the CDC spec lists Ethernet notifications
+ * section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS
+ * RNDIS also defines its own bit-incompatible notifications
+ */
+
+#define USB_CDC_NOTIFY_NETWORK_CONNECTION      0x00
+#define USB_CDC_NOTIFY_RESPONSE_AVAILABLE      0x01
+#define USB_CDC_NOTIFY_SERIAL_STATE            0x20
+#define USB_CDC_NOTIFY_SPEED_CHANGE            0x2a
+
+struct usb_cdc_notification {
+       __u8    bmRequestType;
+       __u8    bNotificationType;
+       __le16  wValue;
+       __le16  wIndex;
+       __le16  wLength;
+} __attribute__ ((packed));
+
diff --git a/include/net/act_generic.h b/include/net/act_generic.h
new file mode 100644 (file)
index 0000000..c9daa7e
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * include/net/act_generic.h
+ *
+*/
+#ifndef _NET_ACT_GENERIC_H
+#define _NET_ACT_GENERIC_H
+static inline int tcf_defact_release(struct tcf_defact *p, int bind)
+{
+       int ret = 0;
+       if (p) {
+               if (bind) {
+                       p->bindcnt--;
+               }
+               p->refcnt--;
+               if (p->bindcnt <= 0 && p->refcnt <= 0) {
+                       kfree(p->defdata);
+                       tcf_hash_destroy(p);
+                       ret = 1;
+               }
+       }
+       return ret;
+}
+
+static inline int
+alloc_defdata(struct tcf_defact *p, u32 datalen, void *defdata)
+{
+       p->defdata = kmalloc(datalen, GFP_KERNEL);
+       if (p->defdata == NULL)
+               return -ENOMEM;
+       p->datalen = datalen;
+       memcpy(p->defdata, defdata, datalen);
+       return 0;
+}
+
+static inline int
+realloc_defdata(struct tcf_defact *p, u32 datalen, void *defdata)
+{
+       /* safer to be just brute force for now */
+       kfree(p->defdata);
+       return alloc_defdata(p, datalen, defdata);
+}
+
+static inline int
+tcf_defact_init(struct rtattr *rta, struct rtattr *est,
+               struct tc_action *a, int ovr, int bind)
+{
+       struct rtattr *tb[TCA_DEF_MAX];
+       struct tc_defact *parm;
+       struct tcf_defact *p;
+       void *defdata;
+       u32 datalen = 0;
+       int ret = 0;
+
+       if (rta == NULL || rtattr_parse_nested(tb, TCA_DEF_MAX, rta) < 0)
+               return -EINVAL;
+
+       if (tb[TCA_DEF_PARMS - 1] == NULL || 
+           RTA_PAYLOAD(tb[TCA_DEF_PARMS - 1]) < sizeof(*parm))
+               return -EINVAL;
+
+       parm = RTA_DATA(tb[TCA_DEF_PARMS - 1]);
+       defdata = RTA_DATA(tb[TCA_DEF_DATA - 1]);
+       if (defdata == NULL)
+               return -EINVAL;
+
+       datalen = RTA_PAYLOAD(tb[TCA_DEF_DATA - 1]);
+       if (datalen <= 0)
+               return -EINVAL;
+
+       p = tcf_hash_check(parm->index, a, ovr, bind);
+       if (p == NULL) {
+               p = tcf_hash_create(parm->index, est, a, sizeof(*p), ovr, bind);
+               if (p == NULL)
+                       return -ENOMEM;
+
+               ret = alloc_defdata(p, datalen, defdata);
+               if (ret < 0) {
+                       kfree(p);
+                       return ret;
+               }
+               ret = ACT_P_CREATED;
+       } else {
+               if (!ovr) {
+                       tcf_defact_release(p, bind);
+                       return -EEXIST;
+               }
+               realloc_defdata(p, datalen, defdata);
+       }
+
+       spin_lock_bh(&p->lock);
+       p->action = parm->action;
+       spin_unlock_bh(&p->lock);
+       if (ret == ACT_P_CREATED)
+               tcf_hash_insert(p);
+       return ret;
+}
+
+static inline int tcf_defact_cleanup(struct tc_action *a, int bind)
+{
+       struct tcf_defact *p = PRIV(a, defact);
+
+       if (p != NULL)
+               return tcf_defact_release(p, bind);
+       return 0;
+}
+
+static inline int
+tcf_defact_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
+{
+       unsigned char *b = skb->tail;
+       struct tc_defact opt;
+       struct tcf_defact *p = PRIV(a, defact);
+       struct tcf_t t;
+
+       opt.index = p->index;
+       opt.refcnt = p->refcnt - ref;
+       opt.bindcnt = p->bindcnt - bind;
+       opt.action = p->action;
+       RTA_PUT(skb, TCA_DEF_PARMS, sizeof(opt), &opt);
+       RTA_PUT(skb, TCA_DEF_DATA, p->datalen, p->defdata);
+       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_DEF_TM, sizeof(t), &t);
+       return skb->len;
+
+rtattr_failure:
+       skb_trim(skb, b - skb->data);
+       return -1;
+}
+
+#define tca_use_default_ops \
+       .dump           =       tcf_defact_dump, \
+       .cleanup        =       tcf_defact_cleanup, \
+       .init           =       tcf_defact_init, \
+       .walk           =       tcf_generic_walker, \
+
+#define tca_use_default_defines(name) \
+       static u32 idx_gen; \
+       static struct tcf_defact *tcf_##name_ht[MY_TAB_SIZE]; \
+       static DEFINE_RWLOCK(##name_lock);
+#endif /* _NET_ACT_GENERIC_H */
diff --git a/include/net/ip_mp_alg.h b/include/net/ip_mp_alg.h
new file mode 100644 (file)
index 0000000..7722573
--- /dev/null
@@ -0,0 +1,99 @@
+/* ip_mp_alg.h: IPV4 multipath algorithm support.
+ *
+ * Copyright (C) 2004, 2005 Einar Lueck <elueck@de.ibm.com>
+ * Copyright (C) 2005 David S. Miller <davem@davemloft.net>
+ */
+
+#ifndef _NET_IP_MP_ALG_H
+#define _NET_IP_MP_ALG_H
+
+#include <linux/config.h>
+#include <linux/ip_mp_alg.h>
+#include <net/flow.h>
+#include <net/route.h>
+
+struct fib_nh;
+
+struct ip_mp_alg_ops {
+       void    (*mp_alg_select_route)(const struct flowi *flp,
+                                      struct rtable *rth, struct rtable **rp);
+       void    (*mp_alg_flush)(void);
+       void    (*mp_alg_set_nhinfo)(__u32 network, __u32 netmask,
+                                    unsigned char prefixlen,
+                                    const struct fib_nh *nh);
+       void    (*mp_alg_remove)(struct rtable *rth);
+};
+
+extern int multipath_alg_register(struct ip_mp_alg_ops *, enum ip_mp_alg);
+extern void multipath_alg_unregister(struct ip_mp_alg_ops *, enum ip_mp_alg);
+
+extern struct ip_mp_alg_ops *ip_mp_alg_table[];
+
+static inline int multipath_select_route(const struct flowi *flp,
+                                        struct rtable *rth,
+                                        struct rtable **rp)
+{
+#ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED
+       struct ip_mp_alg_ops *ops = ip_mp_alg_table[rth->rt_multipath_alg];
+
+       /* mp_alg_select_route _MUST_ be implemented */
+       if (ops && (rth->u.dst.flags & DST_BALANCED)) {
+               ops->mp_alg_select_route(flp, rth, rp);
+               return 1;
+       }
+#endif
+       return 0;
+}
+
+static inline void multipath_flush(void)
+{
+#ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED
+       int i;
+
+       for (i = IP_MP_ALG_NONE; i <= IP_MP_ALG_MAX; i++) {
+               struct ip_mp_alg_ops *ops = ip_mp_alg_table[i];
+
+               if (ops && ops->mp_alg_flush)
+                       ops->mp_alg_flush();
+       }
+#endif
+}
+
+static inline void multipath_set_nhinfo(struct rtable *rth,
+                                       __u32 network, __u32 netmask,
+                                       unsigned char prefixlen,
+                                       const struct fib_nh *nh)
+{
+#ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED
+       struct ip_mp_alg_ops *ops = ip_mp_alg_table[rth->rt_multipath_alg];
+
+       if (ops && ops->mp_alg_set_nhinfo)
+               ops->mp_alg_set_nhinfo(network, netmask, prefixlen, nh);
+#endif
+}
+
+static inline void multipath_remove(struct rtable *rth)
+{
+#ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED
+       struct ip_mp_alg_ops *ops = ip_mp_alg_table[rth->rt_multipath_alg];
+
+       if (ops && ops->mp_alg_remove &&
+           (rth->u.dst.flags & DST_BALANCED))
+               ops->mp_alg_remove(rth);
+#endif
+}
+
+static inline int multipath_comparekeys(const struct flowi *flp1,
+                                       const struct flowi *flp2)
+{
+       return flp1->fl4_dst == flp2->fl4_dst &&
+               flp1->fl4_src == flp2->fl4_src &&
+               flp1->oif == flp2->oif &&
+#ifdef CONFIG_IP_ROUTE_FWMARK
+               flp1->fl4_fwmark == flp2->fl4_fwmark &&
+#endif
+               !((flp1->fl4_tos ^ flp2->fl4_tos) &
+                 (IPTOS_RT_MASK | RTO_ONLINK));
+}
+
+#endif /* _NET_IP_MP_ALG_H */
diff --git a/include/net/tc_act/tc_defact.h b/include/net/tc_act/tc_defact.h
new file mode 100644 (file)
index 0000000..463aa67
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __NET_TC_DEF_H
+#define __NET_TC_DEF_H
+
+#include <net/act_api.h>
+
+struct tcf_defact
+{
+       tca_gen(defact);
+       u32     datalen;
+       void    *defdata;
+};
+
+#endif
diff --git a/include/sound/ak4114.h b/include/sound/ak4114.h
new file mode 100644 (file)
index 0000000..f3f2c3e
--- /dev/null
@@ -0,0 +1,205 @@
+#ifndef __SOUND_AK4114_H
+#define __SOUND_AK4114_H
+
+/*
+ *  Routines for Asahi Kasei AK4114
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.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.
+ *
+ *   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
+ *
+ */
+
+/* AK4114 registers */
+#define AK4114_REG_PWRDN       0x00    /* power down */
+#define AK4114_REG_FORMAT      0x01    /* format control */
+#define AK4114_REG_IO0         0x02    /* input/output control */
+#define AK4114_REG_IO1         0x03    /* input/output control */
+#define AK4114_REG_INT0_MASK   0x04    /* interrupt0 mask */
+#define AK4114_REG_INT1_MASK   0x05    /* interrupt1 mask */
+#define AK4114_REG_RCS0                0x06    /* receiver status 0 */
+#define AK4114_REG_RCS1                0x07    /* receiver status 1 */
+#define AK4114_REG_RXCSB0      0x08    /* RX channel status byte 0 */
+#define AK4114_REG_RXCSB1      0x09    /* RX channel status byte 1 */
+#define AK4114_REG_RXCSB2      0x0a    /* RX channel status byte 2 */
+#define AK4114_REG_RXCSB3      0x0b    /* RX channel status byte 3 */
+#define AK4114_REG_RXCSB4      0x0c    /* RX channel status byte 4 */
+#define AK4114_REG_TXCSB0      0x0d    /* TX channel status byte 0 */
+#define AK4114_REG_TXCSB1      0x0e    /* TX channel status byte 1 */
+#define AK4114_REG_TXCSB2      0x0f    /* TX channel status byte 2 */
+#define AK4114_REG_TXCSB3      0x10    /* TX channel status byte 3 */
+#define AK4114_REG_TXCSB4      0x11    /* TX channel status byte 4 */
+#define AK4114_REG_Pc0         0x12    /* burst preamble Pc byte 0 */
+#define AK4114_REG_Pc1         0x13    /* burst preamble Pc byte 1 */
+#define AK4114_REG_Pd0         0x14    /* burst preamble Pd byte 0 */
+#define AK4114_REG_Pd1         0x15    /* burst preamble Pd byte 1 */
+#define AK4114_REG_QSUB_ADDR   0x16    /* Q-subcode address + control */
+#define AK4114_REG_QSUB_TRACK  0x17    /* Q-subcode track */
+#define AK4114_REG_QSUB_INDEX  0x18    /* Q-subcode index */
+#define AK4114_REG_QSUB_MINUTE 0x19    /* Q-subcode minute */
+#define AK4114_REG_QSUB_SECOND 0x1a    /* Q-subcode second */
+#define AK4114_REG_QSUB_FRAME  0x1b    /* Q-subcode frame */
+#define AK4114_REG_QSUB_ZERO   0x1c    /* Q-subcode zero */
+#define AK4114_REG_QSUB_ABSMIN 0x1d    /* Q-subcode absolute minute */
+#define AK4114_REG_QSUB_ABSSEC 0x1e    /* Q-subcode absolute second */
+#define AK4114_REG_QSUB_ABSFRM 0x1f    /* Q-subcode absolute frame */
+
+/* sizes */
+#define AK4114_REG_RXCSB_SIZE  ((AK4114_REG_RXCSB4-AK4114_REG_RXCSB0)+1)
+#define AK4114_REG_TXCSB_SIZE  ((AK4114_REG_TXCSB4-AK4114_REG_TXCSB0)+1)
+#define AK4114_REG_QSUB_SIZE   ((AK4114_REG_QSUB_ABSFRM-AK4114_REG_QSUB_ADDR)+1)
+
+/* AK4117_REG_PWRDN bits */
+#define AK4114_CS12            (1<<7)  /* Channel Status Select */
+#define AK4114_BCU             (1<<6)  /* Block Start & C/U Output Mode */
+#define AK4114_CM1             (1<<5)  /* Master Clock Operation Select */
+#define AK4114_CM0             (1<<4)  /* Master Clock Operation Select */
+#define AK4114_OCKS1           (1<<3)  /* Master Clock Frequency Select */
+#define AK4114_OCKS0           (1<<2)  /* Master Clock Frequency Select */
+#define AK4114_PWN             (1<<1)  /* 0 = power down, 1 = normal operation */
+#define AK4114_RST             (1<<0)  /* 0 = reset & initialize (except this register), 1 = normal operation */
+
+/* AK4114_REQ_FORMAT bits */
+#define AK4114_MONO            (1<<7)  /* Double Sampling Frequency Mode: 0 = stereo, 1 = mono */
+#define AK4114_DIF2            (1<<5)  /* Audio Data Control */
+#define AK4114_DIF1            (1<<5)  /* Audio Data Control */
+#define AK4114_DIF0            (1<<4)  /* Audio Data Control */
+#define AK4114_DIF_16R         (0)                             /* STDO: 16-bit, right justified */
+#define AK4114_DIF_18R         (AK4114_DIF0)                   /* STDO: 18-bit, right justified */
+#define AK4114_DIF_20R         (AK4114_DIF1)                   /* STDO: 20-bit, right justified */
+#define AK4114_DIF_24R         (AK4114_DIF1|AK4114_DIF0)       /* STDO: 24-bit, right justified */
+#define AK4114_DIF_24L         (AK4114_DIF2)                   /* STDO: 24-bit, left justified */
+#define AK4114_DIF_24I2S       (AK4114_DIF2|AK4114_DIF0)       /* STDO: I2S */
+#define AK4114_DIF_I24L                (AK4114_DIF2|AK4114_DIF1)       /* STDO: 24-bit, left justified; LRCLK, BICK = Input */
+#define AK4114_DIF_I24I2S      (AK4114_DIF2|AK4114_DIF1|AK4114_DIF0) /* STDO: I2S;  LRCLK, BICK = Input */
+#define AK4114_DEAU            (1<<3)  /* Deemphasis Autodetect Enable (1 = enable) */
+#define AK4114_DEM1            (1<<2)  /* 32kHz-48kHz Deemphasis Control */
+#define AK4114_DEM0            (1<<1)  /* 32kHz-48kHz Deemphasis Control */
+#define AK4114_DEM_44KHZ       (0)
+#define AK4114_DEM_48KHZ       (AK4114_DEM1)
+#define AK4114_DEM_32KHZ       (AK4114_DEM0|AK4114_DEM1)
+#define AK4114_DEM_96KHZ       (AK4114_DEM1)   /* DFS must be set */
+#define AK4114_DFS             (1<<0)  /* 96kHz Deemphasis Control */
+
+/* AK4114_REG_IO0 */
+#define AK4114_TX1E            (1<<7)  /* TX1 Output Enable (1 = enable) */
+#define AK4114_OPS12           (1<<2)  /* Output Though Data Selector for TX1 pin */
+#define AK4114_OPS11           (1<<1)  /* Output Though Data Selector for TX1 pin */
+#define AK4114_OPS10           (1<<0)  /* Output Though Data Selector for TX1 pin */
+#define AK4114_TX0E            (1<<3)  /* TX0 Output Enable (1 = enable) */
+#define AK4114_OPS02           (1<<2)  /* Output Though Data Selector for TX0 pin */
+#define AK4114_OPS01           (1<<1)  /* Output Though Data Selector for TX0 pin */
+#define AK4114_OPS00           (1<<0)  /* Output Though Data Selector for TX0 pin */
+
+/* AK4114_REG_IO1 */
+#define AK4114_EFH1            (1<<7)  /* Interrupt 0 pin Hold */
+#define AK4114_EFH0            (1<<6)  /* Interrupt 0 pin Hold */
+#define AK4114_EFH_512         (0)
+#define AK4114_EFH_1024                (AK4114_EFH0)
+#define AK4114_EFH_2048                (AK4114_EFH1)
+#define AK4114_EFH_4096                (AK4114_EFH1|AK4114_EFH0)
+#define AK4114_UDIT            (1<<5)  /* U-bit Control for DIT (0 = fixed '0', 1 = recovered) */
+#define AK4114_TLR             (1<<4)  /* Double Sampling Frequency Select for DIT (0 = L channel, 1 = R channel) */
+#define AK4114_DIT             (1<<3)  /* TX1 out: 0 = Through Data (RX data), 1 = Transmit Data (DAUX data) */
+#define AK4114_IPS2            (1<<2)  /* Input Recovery Data Select */
+#define AK4114_IPS1            (1<<1)  /* Input Recovery Data Select */
+#define AK4114_IPS0            (1<<0)  /* Input Recovery Data Select */
+#define AK4114_IPS(x)          ((x)&7)
+
+/* AK4114_REG_INT0_MASK && AK4114_REG_INT1_MASK*/
+#define AK4117_MQI              (1<<7)  /* mask enable for QINT bit */
+#define AK4117_MAT              (1<<6)  /* mask enable for AUTO bit */
+#define AK4117_MCI              (1<<5)  /* mask enable for CINT bit */
+#define AK4117_MUL              (1<<4)  /* mask enable for UNLOCK bit */
+#define AK4117_MDTS             (1<<3)  /* mask enable for DTSCD bit */
+#define AK4117_MPE              (1<<2)  /* mask enable for PEM bit */
+#define AK4117_MAN              (1<<1)  /* mask enable for AUDN bit */
+#define AK4117_MPR              (1<<0)  /* mask enable for PAR bit */
+
+/* AK4114_REG_RCS0 */
+#define AK4114_QINT            (1<<7)  /* Q-subcode buffer interrupt, 0 = no change, 1 = changed */
+#define AK4114_AUTO            (1<<6)  /* Non-PCM or DTS stream auto detection, 0 = no detect, 1 = detect */
+#define AK4114_CINT            (1<<5)  /* channel status buffer interrupt, 0 = no change, 1 = change */
+#define AK4114_UNLCK           (1<<4)  /* PLL lock status, 0 = lock, 1 = unlock */
+#define AK4114_DTSCD           (1<<3)  /* DTS-CD Detect, 0 = No detect, 1 = Detect */
+#define AK4114_PEM             (1<<2)  /* Pre-emphasis Detect, 0 = OFF, 1 = ON */
+#define AK4114_AUDION          (1<<1)  /* audio bit output, 0 = audio, 1 = non-audio */
+#define AK4114_PAR             (1<<0)  /* parity error or biphase error status, 0 = no error, 1 = error */
+
+/* AK4114_REG_RCS1 */
+#define AK4114_FS3             (1<<7)  /* sampling frequency detection */
+#define AK4114_FS2             (1<<6)
+#define AK4114_FS1             (1<<5)
+#define AK4114_FS0             (1<<4)
+#define AK4114_FS_44100HZ      (0)
+#define AK4114_FS_48000HZ      (AK4114_FS1)
+#define AK4114_FS_32000HZ      (AK4114_FS1|AK4114_FS0)
+#define AK4114_FS_88200HZ      (AK4114_FS3)
+#define AK4114_FS_96000HZ      (AK4114_FS3|AK4114_FS1)
+#define AK4114_FS_176400HZ     (AK4114_FS3|AK4114_FS2)
+#define AK4114_FS_192000HZ     (AK4114_FS3|AK4114_FS2|AK4114_FS1)
+#define AK4114_V               (1<<3)  /* Validity of Channel Status, 0 = Valid, 1 = Invalid */
+#define AK4114_QCRC            (1<<1)  /* CRC for Q-subcode, 0 = no error, 1 = error */
+#define AK4114_CCRC            (1<<0)  /* CRC for channel status, 0 = no error, 1 = error */
+
+/* flags for snd_ak4114_check_rate_and_errors() */
+#define AK4114_CHECK_NO_STAT   (1<<0)  /* no statistics */
+#define AK4114_CHECK_NO_RATE   (1<<1)  /* no rate check */
+
+#define AK4114_CONTROLS                14
+
+typedef void (ak4114_write_t)(void *private_data, unsigned char addr, unsigned char data);
+typedef unsigned char (ak4114_read_t)(void *private_data, unsigned char addr);
+
+typedef struct ak4114 ak4114_t;
+
+struct ak4114 {
+       snd_card_t * card;
+       ak4114_write_t * write;
+       ak4114_read_t * read;
+       void * private_data;
+       unsigned int init: 1;
+       spinlock_t lock;
+       unsigned char regmap[7];
+       unsigned char txcsb[5];
+       snd_kcontrol_t *kctls[AK4114_CONTROLS];
+       snd_pcm_substream_t *playback_substream;
+       snd_pcm_substream_t *capture_substream;
+       unsigned long parity_errors;
+       unsigned long v_bit_errors;
+       unsigned long qcrc_errors;
+       unsigned long ccrc_errors;
+       unsigned char rcs0;
+       unsigned char rcs1;
+       struct workqueue_struct *workqueue;
+       struct work_struct work;
+       void *change_callback_private;
+       void (*change_callback)(ak4114_t *ak4114, unsigned char c0, unsigned char c1);
+};
+
+int snd_ak4114_create(snd_card_t *card,
+                     ak4114_read_t *read, ak4114_write_t *write,
+                     unsigned char pgm[7], unsigned char txcsb[5],
+                     void *private_data, ak4114_t **r_ak4114);
+void snd_ak4114_reg_write(ak4114_t *ak4114, unsigned char reg, unsigned char mask, unsigned char val);
+void snd_ak4114_reinit(ak4114_t *ak4114);
+int snd_ak4114_build(ak4114_t *ak4114,
+                    snd_pcm_substream_t *playback_substream,
+                     snd_pcm_substream_t *capture_substream);
+int snd_ak4114_external_rate(ak4114_t *ak4114);
+int snd_ak4114_check_rate_and_errors(ak4114_t *ak4114, unsigned int flags);
+
+#endif /* __SOUND_AK4114_H */
+
diff --git a/include/video/s1d13xxxfb.h b/include/video/s1d13xxxfb.h
new file mode 100644 (file)
index 0000000..f06cc88
--- /dev/null
@@ -0,0 +1,166 @@
+/* drivers/video/s1d3xxxfb.h
+ *
+ * (c) 2004 Simtec Electronics
+ * (c) 2005 Thibaut VARENE <varenet@parisc-linux.org>
+ *
+ * Header file for Epson S1D13XXX driver code
+ *
+ * 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        S1D13XXXFB_H
+#define        S1D13XXXFB_H
+
+#define S1D_PALETTE_SIZE               256
+#define S1D_CHIP_REV                   7       /* expected chip revision number for s1d13806 */
+#define S1D_FBID                       "S1D13806"
+#define S1D_DEVICENAME                 "s1d13806fb"
+
+/* register definitions (tested on s1d13896) */
+#define S1DREG_REV_CODE                        0x0000  /* Revision Code Register */
+#define S1DREG_MISC                    0x0001  /* Miscellaneous Register */
+#define S1DREG_GPIO_CNF0               0x0004  /* General IO Pins Configuration Register 0 */
+#define S1DREG_GPIO_CNF1               0x0005  /* General IO Pins Configuration Register 1 */
+#define S1DREG_GPIO_CTL0               0x0008  /* General IO Pins Control Register 0 */
+#define S1DREG_GPIO_CTL1               0x0009  /* General IO Pins Control Register 1 */
+#define S1DREG_CNF_STATUS              0x000C  /* Configuration Status Readback Register */
+#define S1DREG_CLK_CNF                 0x0010  /* Memory Clock Configuration Register */
+#define S1DREG_LCD_CLK_CNF             0x0014  /* LCD Pixel Clock Configuration Register */
+#define S1DREG_CRT_CLK_CNF             0x0018  /* CRT/TV Pixel Clock Configuration Register */
+#define S1DREG_MPLUG_CLK_CNF           0x001C  /* MediaPlug Clock Configuration Register */
+#define S1DREG_CPU2MEM_WST_SEL         0x001E  /* CPU To Memory Wait State Select Register */
+#define S1DREG_MEM_CNF                 0x0020  /* Memory Configuration Register */
+#define S1DREG_SDRAM_REF_RATE          0x0021  /* SDRAM Refresh Rate Register */
+#define S1DREG_SDRAM_TC0               0x002A  /* SDRAM Timing Control Register 0 */
+#define S1DREG_SDRAM_TC1               0x002B  /* SDRAM Timing Control Register 1 */
+#define S1DREG_PANEL_TYPE              0x0030  /* Panel Type Register */
+#define S1DREG_MOD_RATE                        0x0031  /* MOD Rate Register */
+#define S1DREG_LCD_DISP_HWIDTH         0x0032  /* LCD Horizontal Display Width Register: ((val)+1)*8)=pix/line */
+#define S1DREG_LCD_NDISP_HPER          0x0034  /* LCD Horizontal Non-Display Period Register: ((val)+1)*8)=NDpix/line */
+#define S1DREG_TFT_FPLINE_START                0x0035  /* TFT FPLINE Start Position Register */
+#define S1DREG_TFT_FPLINE_PWIDTH       0x0036  /* TFT FPLINE Pulse Width Register. */
+#define S1DREG_LCD_DISP_VHEIGHT0       0x0038  /* LCD Vertical Display Height Register 0 */
+#define S1DREG_LCD_DISP_VHEIGHT1       0x0039  /* LCD Vertical Display Height Register 1 */
+#define S1DREG_LCD_NDISP_VPER          0x003A  /* LCD Vertical Non-Display Period Register: (val)+1=NDlines */
+#define S1DREG_TFT_FPFRAME_START       0x003B  /* TFT FPFRAME Start Position Register */
+#define S1DREG_TFT_FPFRAME_PWIDTH      0x003C  /* TFT FPFRAME Pulse Width Register */
+#define S1DREG_LCD_DISP_MODE           0x0040  /* LCD Display Mode Register */
+#define S1DREG_LCD_MISC                        0x0041  /* LCD Miscellaneous Register */
+#define S1DREG_LCD_DISP_START0         0x0042  /* LCD Display Start Address Register 0 */
+#define S1DREG_LCD_DISP_START1         0x0043  /* LCD Display Start Address Register 1 */
+#define S1DREG_LCD_DISP_START2         0x0044  /* LCD Display Start Address Register 2 */
+#define S1DREG_LCD_MEM_OFF0            0x0046  /* LCD Memory Address Offset Register 0 */
+#define S1DREG_LCD_MEM_OFF1            0x0047  /* LCD Memory Address Offset Register 1 */
+#define S1DREG_LCD_PIX_PAN             0x0048  /* LCD Pixel Panning Register */
+#define S1DREG_LCD_DISP_FIFO_HTC       0x004A  /* LCD Display FIFO High Threshold Control Register */
+#define S1DREG_LCD_DISP_FIFO_LTC       0x004B  /* LCD Display FIFO Low Threshold Control Register */
+#define S1DREG_CRT_DISP_HWIDTH         0x0050  /* CRT/TV Horizontal Display Width Register: ((val)+1)*8)=pix/line */
+#define S1DREG_CRT_NDISP_HPER          0x0052  /* CRT/TV Horizontal Non-Display Period Register */
+#define S1DREG_CRT_HRTC_START          0x0053  /* CRT/TV HRTC Start Position Register */
+#define S1DREG_CRT_HRTC_PWIDTH         0x0054  /* CRT/TV HRTC Pulse Width Register */
+#define S1DREG_CRT_DISP_VHEIGHT0       0x0056  /* CRT/TV Vertical Display Height Register 0 */
+#define S1DREG_CRT_DISP_VHEIGHT1       0x0057  /* CRT/TV Vertical Display Height Register 1 */
+#define S1DREG_CRT_NDISP_VPER          0x0058  /* CRT/TV Vertical Non-Display Period Register */
+#define S1DREG_CRT_VRTC_START          0x0059  /* CRT/TV VRTC Start Position Register */
+#define S1DREG_CRT_VRTC_PWIDTH         0x005A  /* CRT/TV VRTC Pulse Width Register */
+#define S1DREG_TV_OUT_CTL              0x005B  /* TV Output Control Register */
+#define S1DREG_CRT_DISP_MODE           0x0060  /* CRT/TV Display Mode Register */
+#define S1DREG_CRT_DISP_START0         0x0062  /* CRT/TV Display Start Address Register 0 */
+#define S1DREG_CRT_DISP_START1         0x0063  /* CRT/TV Display Start Address Register 1 */
+#define S1DREG_CRT_DISP_START2         0x0064  /* CRT/TV Display Start Address Register 2 */
+#define S1DREG_CRT_MEM_OFF0            0x0066  /* CRT/TV Memory Address Offset Register 0 */
+#define S1DREG_CRT_MEM_OFF1            0x0067  /* CRT/TV Memory Address Offset Register 1 */
+#define S1DREG_CRT_PIX_PAN             0x0068  /* CRT/TV Pixel Panning Register */
+#define S1DREG_CRT_DISP_FIFO_HTC       0x006A  /* CRT/TV Display FIFO High Threshold Control Register */
+#define S1DREG_CRT_DISP_FIFO_LTC       0x006B  /* CRT/TV Display FIFO Low Threshold Control Register */
+#define S1DREG_LCD_CUR_CTL             0x0070  /* LCD Ink/Cursor Control Register */
+#define S1DREG_LCD_CUR_START           0x0071  /* LCD Ink/Cursor Start Address Register */
+#define S1DREG_LCD_CUR_XPOS0           0x0072  /* LCD Cursor X Position Register 0 */
+#define S1DREG_LCD_CUR_XPOS1           0x0073  /* LCD Cursor X Position Register 1 */
+#define S1DREG_LCD_CUR_YPOS0           0x0074  /* LCD Cursor Y Position Register 0 */
+#define S1DREG_LCD_CUR_YPOS1           0x0075  /* LCD Cursor Y Position Register 1 */
+#define S1DREG_LCD_CUR_BCTL0           0x0076  /* LCD Ink/Cursor Blue Color 0 Register */
+#define S1DREG_LCD_CUR_GCTL0           0x0077  /* LCD Ink/Cursor Green Color 0 Register */
+#define S1DREG_LCD_CUR_RCTL0           0x0078  /* LCD Ink/Cursor Red Color 0 Register */
+#define S1DREG_LCD_CUR_BCTL1           0x007A  /* LCD Ink/Cursor Blue Color 1 Register */
+#define S1DREG_LCD_CUR_GCTL1           0x007B  /* LCD Ink/Cursor Green Color 1 Register */
+#define S1DREG_LCD_CUR_RCTL1           0x007C  /* LCD Ink/Cursor Red Color 1 Register */
+#define S1DREG_LCD_CUR_FIFO_HTC                0x007E  /* LCD Ink/Cursor FIFO High Threshold Register */
+#define S1DREG_CRT_CUR_CTL             0x0080  /* CRT/TV Ink/Cursor Control Register */
+#define S1DREG_CRT_CUR_START           0x0081  /* CRT/TV Ink/Cursor Start Address Register */
+#define S1DREG_CRT_CUR_XPOS0           0x0082  /* CRT/TV Cursor X Position Register 0 */
+#define S1DREG_CRT_CUR_XPOS1           0x0083  /* CRT/TV Cursor X Position Register 1 */
+#define S1DREG_CRT_CUR_YPOS0           0x0084  /* CRT/TV Cursor Y Position Register 0 */
+#define S1DREG_CRT_CUR_YPOS1           0x0085  /* CRT/TV Cursor Y Position Register 1 */
+#define S1DREG_CRT_CUR_BCTL0           0x0086  /* CRT/TV Ink/Cursor Blue Color 0 Register */
+#define S1DREG_CRT_CUR_GCTL0           0x0087  /* CRT/TV Ink/Cursor Green Color 0 Register */
+#define S1DREG_CRT_CUR_RCTL0           0x0088  /* CRT/TV Ink/Cursor Red Color 0 Register */
+#define S1DREG_CRT_CUR_BCTL1           0x008A  /* CRT/TV Ink/Cursor Blue Color 1 Register */
+#define S1DREG_CRT_CUR_GCTL1           0x008B  /* CRT/TV Ink/Cursor Green Color 1 Register */
+#define S1DREG_CRT_CUR_RCTL1           0x008C  /* CRT/TV Ink/Cursor Red Color 1 Register */
+#define S1DREG_CRT_CUR_FIFO_HTC                0x008E  /* CRT/TV Ink/Cursor FIFO High Threshold Register */
+#define S1DREG_BBLT_CTL0               0x0100  /* BitBLT Control Register 0 */
+#define S1DREG_BBLT_CTL1               0x0101  /* BitBLT Control Register 1 */
+#define S1DREG_BBLT_CC_EXP             0x0102  /* BitBLT Code/Color Expansion Register */
+#define S1DREG_BBLT_OP                 0x0103  /* BitBLT Operation Register */
+#define S1DREG_BBLT_SRC_START0         0x0104  /* BitBLT Source Start Address Register 0 */
+#define S1DREG_BBLT_SRC_START1         0x0105  /* BitBLT Source Start Address Register 1 */
+#define S1DREG_BBLT_SRC_START2         0x0106  /* BitBLT Source Start Address Register 2 */
+#define S1DREG_BBLT_DST_START0         0x0108  /* BitBLT Destination Start Address Register 0 */
+#define S1DREG_BBLT_DST_START1         0x0109  /* BitBLT Destination Start Address Register 1 */
+#define S1DREG_BBLT_DST_START2         0x010A  /* BitBLT Destination Start Address Register 2 */
+#define S1DREG_BBLT_MEM_OFF0           0x010C  /* BitBLT Memory Address Offset Register 0 */
+#define S1DREG_BBLT_MEM_OFF1           0x010D  /* BitBLT Memory Address Offset Register 1 */
+#define S1DREG_BBLT_WIDTH0             0x0110  /* BitBLT Width Register 0 */
+#define S1DREG_BBLT_WIDTH1             0x0111  /* BitBLT Width Register 1 */
+#define S1DREG_BBLT_HEIGHT0            0x0112  /* BitBLT Height Register 0 */
+#define S1DREG_BBLT_HEIGHT1            0x0113  /* BitBLT Height Register 1 */
+#define S1DREG_BBLT_BGC0               0x0114  /* BitBLT Background Color Register 0 */
+#define S1DREG_BBLT_BGC1               0x0115  /* BitBLT Background Color Register 1 */
+#define S1DREG_BBLT_FGC0               0x0118  /* BitBLT Foreground Color Register 0 */
+#define S1DREG_BBLT_FGC1               0x0119  /* BitBLT Foreground Color Register 1 */
+#define S1DREG_LKUP_MODE               0x01E0  /* Look-Up Table Mode Register */
+#define S1DREG_LKUP_ADDR               0x01E2  /* Look-Up Table Address Register */
+#define S1DREG_LKUP_DATA               0x01E4  /* Look-Up Table Data Register */
+#define S1DREG_PS_CNF                  0x01F0  /* Power Save Configuration Register */
+#define S1DREG_PS_STATUS               0x01F1  /* Power Save Status Register */
+#define S1DREG_CPU2MEM_WDOGT           0x01F4  /* CPU-to-Memory Access Watchdog Timer Register */
+#define S1DREG_COM_DISP_MODE           0x01FC  /* Common Display Mode Register */
+
+#define S1DREG_DELAYOFF                        0xFFFE
+#define S1DREG_DELAYON                 0xFFFF
+
+/* Note: all above defines should go in separate header files
+   when implementing other S1D13xxx chip support. */
+
+struct s1d13xxxfb_regval {
+       u16     addr;
+       u8      value;
+};
+
+
+struct s1d13xxxfb_par {
+       void __iomem    *regs;
+       unsigned char   display;
+
+       unsigned int    pseudo_palette[16];
+#ifdef CONFIG_PM
+       void            *regs_save;     /* pm saves all registers here */
+       void            *disp_save;     /* pm saves entire screen here */
+#endif
+};
+
+struct s1d13xxxfb_pdata {
+       const struct s1d13xxxfb_regval  *initregs;
+       const unsigned int              initregssize;
+       void                            (*platform_init_video)(void);
+#ifdef CONFIG_PM
+       int                             (*platform_suspend_video)(void);
+       int                             (*platform_resume_video)(void);
+#endif
+};
+
+#endif
+
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
new file mode 100644 (file)
index 0000000..00e8f25
--- /dev/null
@@ -0,0 +1,1578 @@
+/*
+ *  kernel/cpuset.c
+ *
+ *  Processor and Memory placement constraints for sets of tasks.
+ *
+ *  Copyright (C) 2003 BULL SA.
+ *  Copyright (C) 2004 Silicon Graphics, Inc.
+ *
+ *  Portions derived from Patrick Mochel's sysfs code.
+ *  sysfs is Copyright (c) 2001-3 Patrick Mochel
+ *  Portions Copyright (c) 2004 Silicon Graphics, Inc.
+ *
+ *  2003-10-10 Written by Simon Derr <simon.derr@bull.net>
+ *  2003-10-22 Updates by Stephen Hemminger.
+ *  2004 May-July Rework by Paul Jackson <pj@sgi.com>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of the Linux
+ *  distribution for more details.
+ */
+
+#include <linux/config.h>
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/cpuset.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/pagemap.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/backing-dev.h>
+#include <linux/sort.h>
+
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <asm/semaphore.h>
+
+#define CPUSET_SUPER_MAGIC             0x27e0eb
+
+struct cpuset {
+       unsigned long flags;            /* "unsigned long" so bitops work */
+       cpumask_t cpus_allowed;         /* CPUs allowed to tasks in cpuset */
+       nodemask_t mems_allowed;        /* Memory Nodes allowed to tasks */
+
+       atomic_t count;                 /* count tasks using this cpuset */
+
+       /*
+        * We link our 'sibling' struct into our parents 'children'.
+        * Our children link their 'sibling' into our 'children'.
+        */
+       struct list_head sibling;       /* my parents children */
+       struct list_head children;      /* my children */
+
+       struct cpuset *parent;          /* my parent */
+       struct dentry *dentry;          /* cpuset fs entry */
+
+       /*
+        * Copy of global cpuset_mems_generation as of the most
+        * recent time this cpuset changed its mems_allowed.
+        */
+        int mems_generation;
+};
+
+/* bits in struct cpuset flags field */
+typedef enum {
+       CS_CPU_EXCLUSIVE,
+       CS_MEM_EXCLUSIVE,
+       CS_REMOVED,
+       CS_NOTIFY_ON_RELEASE
+} cpuset_flagbits_t;
+
+/* convenient tests for these bits */
+static inline int is_cpu_exclusive(const struct cpuset *cs)
+{
+       return !!test_bit(CS_CPU_EXCLUSIVE, &cs->flags);
+}
+
+static inline int is_mem_exclusive(const struct cpuset *cs)
+{
+       return !!test_bit(CS_MEM_EXCLUSIVE, &cs->flags);
+}
+
+static inline int is_removed(const struct cpuset *cs)
+{
+       return !!test_bit(CS_REMOVED, &cs->flags);
+}
+
+static inline int notify_on_release(const struct cpuset *cs)
+{
+       return !!test_bit(CS_NOTIFY_ON_RELEASE, &cs->flags);
+}
+
+/*
+ * Increment this atomic integer everytime any cpuset changes its
+ * mems_allowed value.  Users of cpusets can track this generation
+ * number, and avoid having to lock and reload mems_allowed unless
+ * the cpuset they're using changes generation.
+ *
+ * A single, global generation is needed because attach_task() could
+ * reattach a task to a different cpuset, which must not have its
+ * generation numbers aliased with those of that tasks previous cpuset.
+ *
+ * Generations are needed for mems_allowed because one task cannot
+ * modify anothers memory placement.  So we must enable every task,
+ * on every visit to __alloc_pages(), to efficiently check whether
+ * its current->cpuset->mems_allowed has changed, requiring an update
+ * of its current->mems_allowed.
+ */
+static atomic_t cpuset_mems_generation = ATOMIC_INIT(1);
+
+static struct cpuset top_cpuset = {
+       .flags = ((1 << CS_CPU_EXCLUSIVE) | (1 << CS_MEM_EXCLUSIVE)),
+       .cpus_allowed = CPU_MASK_ALL,
+       .mems_allowed = NODE_MASK_ALL,
+       .count = ATOMIC_INIT(0),
+       .sibling = LIST_HEAD_INIT(top_cpuset.sibling),
+       .children = LIST_HEAD_INIT(top_cpuset.children),
+       .parent = NULL,
+       .dentry = NULL,
+       .mems_generation = 0,
+};
+
+static struct vfsmount *cpuset_mount;
+static struct super_block *cpuset_sb = NULL;
+
+/*
+ * cpuset_sem should be held by anyone who is depending on the children
+ * or sibling lists of any cpuset, or performing non-atomic operations
+ * on the flags or *_allowed values of a cpuset, such as raising the
+ * CS_REMOVED flag bit iff it is not already raised, or reading and
+ * conditionally modifying the *_allowed values.  One kernel global
+ * cpuset semaphore should be sufficient - these things don't change
+ * that much.
+ *
+ * The code that modifies cpusets holds cpuset_sem across the entire
+ * operation, from cpuset_common_file_write() down, single threading
+ * all cpuset modifications (except for counter manipulations from
+ * fork and exit) across the system.  This presumes that cpuset
+ * modifications are rare - better kept simple and safe, even if slow.
+ *
+ * The code that reads cpusets, such as in cpuset_common_file_read()
+ * and below, only holds cpuset_sem across small pieces of code, such
+ * as when reading out possibly multi-word cpumasks and nodemasks, as
+ * the risks are less, and the desire for performance a little greater.
+ * The proc_cpuset_show() routine needs to hold cpuset_sem to insure
+ * that no cs->dentry is NULL, as it walks up the cpuset tree to root.
+ *
+ * The hooks from fork and exit, cpuset_fork() and cpuset_exit(), don't
+ * (usually) grab cpuset_sem.  These are the two most performance
+ * critical pieces of code here.  The exception occurs on exit(),
+ * when a task in a notify_on_release cpuset exits.  Then cpuset_sem
+ * is taken, and if the cpuset count is zero, a usermode call made
+ * to /sbin/cpuset_release_agent with the name of the cpuset (path
+ * relative to the root of cpuset file system) as the argument.
+ *
+ * A cpuset can only be deleted if both its 'count' of using tasks is
+ * zero, and its list of 'children' cpusets is empty.  Since all tasks
+ * in the system use _some_ cpuset, and since there is always at least
+ * one task in the system (init, pid == 1), therefore, top_cpuset
+ * always has either children cpusets and/or using tasks.  So no need
+ * for any special hack to ensure that top_cpuset cannot be deleted.
+ */
+
+static DECLARE_MUTEX(cpuset_sem);
+
+/*
+ * A couple of forward declarations required, due to cyclic reference loop:
+ *  cpuset_mkdir -> cpuset_create -> cpuset_populate_dir -> cpuset_add_file
+ *  -> cpuset_create_file -> cpuset_dir_inode_operations -> cpuset_mkdir.
+ */
+
+static int cpuset_mkdir(struct inode *dir, struct dentry *dentry, int mode);
+static int cpuset_rmdir(struct inode *unused_dir, struct dentry *dentry);
+
+static struct backing_dev_info cpuset_backing_dev_info = {
+       .ra_pages = 0,          /* No readahead */
+       .capabilities   = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK,
+};
+
+static struct inode *cpuset_new_inode(mode_t mode)
+{
+       struct inode *inode = new_inode(cpuset_sb);
+
+       if (inode) {
+               inode->i_mode = mode;
+               inode->i_uid = current->fsuid;
+               inode->i_gid = current->fsgid;
+               inode->i_blksize = PAGE_CACHE_SIZE;
+               inode->i_blocks = 0;
+               inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+               inode->i_mapping->backing_dev_info = &cpuset_backing_dev_info;
+       }
+       return inode;
+}
+
+static void cpuset_diput(struct dentry *dentry, struct inode *inode)
+{
+       /* is dentry a directory ? if so, kfree() associated cpuset */
+       if (S_ISDIR(inode->i_mode)) {
+               struct cpuset *cs = dentry->d_fsdata;
+               BUG_ON(!(is_removed(cs)));
+               kfree(cs);
+       }
+       iput(inode);
+}
+
+static struct dentry_operations cpuset_dops = {
+       .d_iput = cpuset_diput,
+};
+
+static struct dentry *cpuset_get_dentry(struct dentry *parent, const char *name)
+{
+       struct qstr qstr;
+       struct dentry *d;
+
+       qstr.name = name;
+       qstr.len = strlen(name);
+       qstr.hash = full_name_hash(name, qstr.len);
+       d = lookup_hash(&qstr, parent);
+       if (!IS_ERR(d))
+               d->d_op = &cpuset_dops;
+       return d;
+}
+
+static void remove_dir(struct dentry *d)
+{
+       struct dentry *parent = dget(d->d_parent);
+
+       d_delete(d);
+       simple_rmdir(parent->d_inode, d);
+       dput(parent);
+}
+
+/*
+ * NOTE : the dentry must have been dget()'ed
+ */
+static void cpuset_d_remove_dir(struct dentry *dentry)
+{
+       struct list_head *node;
+
+       spin_lock(&dcache_lock);
+       node = dentry->d_subdirs.next;
+       while (node != &dentry->d_subdirs) {
+               struct dentry *d = list_entry(node, struct dentry, d_child);
+               list_del_init(node);
+               if (d->d_inode) {
+                       d = dget_locked(d);
+                       spin_unlock(&dcache_lock);
+                       d_delete(d);
+                       simple_unlink(dentry->d_inode, d);
+                       dput(d);
+                       spin_lock(&dcache_lock);
+               }
+               node = dentry->d_subdirs.next;
+       }
+       list_del_init(&dentry->d_child);
+       spin_unlock(&dcache_lock);
+       remove_dir(dentry);
+}
+
+static struct super_operations cpuset_ops = {
+       .statfs = simple_statfs,
+       .drop_inode = generic_delete_inode,
+};
+
+static int cpuset_fill_super(struct super_block *sb, void *unused_data,
+                                                       int unused_silent)
+{
+       struct inode *inode;
+       struct dentry *root;
+
+       sb->s_blocksize = PAGE_CACHE_SIZE;
+       sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+       sb->s_magic = CPUSET_SUPER_MAGIC;
+       sb->s_op = &cpuset_ops;
+       cpuset_sb = sb;
+
+       inode = cpuset_new_inode(S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR);
+       if (inode) {
+               inode->i_op = &simple_dir_inode_operations;
+               inode->i_fop = &simple_dir_operations;
+               /* directories start off with i_nlink == 2 (for "." entry) */
+               inode->i_nlink++;
+       } else {
+               return -ENOMEM;
+       }
+
+       root = d_alloc_root(inode);
+       if (!root) {
+               iput(inode);
+               return -ENOMEM;
+       }
+       sb->s_root = root;
+       return 0;
+}
+
+static struct super_block *cpuset_get_sb(struct file_system_type *fs_type,
+                                       int flags, const char *unused_dev_name,
+                                       void *data)
+{
+       return get_sb_single(fs_type, flags, data, cpuset_fill_super);
+}
+
+static struct file_system_type cpuset_fs_type = {
+       .name = "cpuset",
+       .get_sb = cpuset_get_sb,
+       .kill_sb = kill_litter_super,
+};
+
+/* struct cftype:
+ *
+ * The files in the cpuset filesystem mostly have a very simple read/write
+ * handling, some common function will take care of it. Nevertheless some cases
+ * (read tasks) are special and therefore I define this structure for every
+ * kind of file.
+ *
+ *
+ * When reading/writing to a file:
+ *     - the cpuset to use in file->f_dentry->d_parent->d_fsdata
+ *     - the 'cftype' of the file is file->f_dentry->d_fsdata
+ */
+
+struct cftype {
+       char *name;
+       int private;
+       int (*open) (struct inode *inode, struct file *file);
+       ssize_t (*read) (struct file *file, char __user *buf, size_t nbytes,
+                                                       loff_t *ppos);
+       int (*write) (struct file *file, const char __user *buf, size_t nbytes,
+                                                       loff_t *ppos);
+       int (*release) (struct inode *inode, struct file *file);
+};
+
+static inline struct cpuset *__d_cs(struct dentry *dentry)
+{
+       return dentry->d_fsdata;
+}
+
+static inline struct cftype *__d_cft(struct dentry *dentry)
+{
+       return dentry->d_fsdata;
+}
+
+/*
+ * Call with cpuset_sem held.  Writes path of cpuset into buf.
+ * Returns 0 on success, -errno on error.
+ */
+
+static int cpuset_path(const struct cpuset *cs, char *buf, int buflen)
+{
+       char *start;
+
+       start = buf + buflen;
+
+       *--start = '\0';
+       for (;;) {
+               int len = cs->dentry->d_name.len;
+               if ((start -= len) < buf)
+                       return -ENAMETOOLONG;
+               memcpy(start, cs->dentry->d_name.name, len);
+               cs = cs->parent;
+               if (!cs)
+                       break;
+               if (!cs->parent)
+                       continue;
+               if (--start < buf)
+                       return -ENAMETOOLONG;
+               *start = '/';
+       }
+       memmove(buf, start, buf + buflen - start);
+       return 0;
+}
+
+/*
+ * Notify userspace when a cpuset is released, by running
+ * /sbin/cpuset_release_agent with the name of the cpuset (path
+ * relative to the root of cpuset file system) as the argument.
+ *
+ * Most likely, this user command will try to rmdir this cpuset.
+ *
+ * This races with the possibility that some other task will be
+ * attached to this cpuset before it is removed, or that some other
+ * user task will 'mkdir' a child cpuset of this cpuset.  That's ok.
+ * The presumed 'rmdir' will fail quietly if this cpuset is no longer
+ * unused, and this cpuset will be reprieved from its death sentence,
+ * to continue to serve a useful existence.  Next time it's released,
+ * we will get notified again, if it still has 'notify_on_release' set.
+ *
+ * Note final arg to call_usermodehelper() is 0 - that means
+ * don't wait.  Since we are holding the global cpuset_sem here,
+ * and we are asking another thread (started from keventd) to rmdir a
+ * cpuset, we can't wait - or we'd deadlock with the removing thread
+ * on cpuset_sem.
+ */
+
+static int cpuset_release_agent(char *cpuset_str)
+{
+       char *argv[3], *envp[3];
+       int i;
+
+       i = 0;
+       argv[i++] = "/sbin/cpuset_release_agent";
+       argv[i++] = cpuset_str;
+       argv[i] = NULL;
+
+       i = 0;
+       /* minimal command environment */
+       envp[i++] = "HOME=/";
+       envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+       envp[i] = NULL;
+
+       return call_usermodehelper(argv[0], argv, envp, 0);
+}
+
+/*
+ * Either cs->count of using tasks transitioned to zero, or the
+ * cs->children list of child cpusets just became empty.  If this
+ * cs is notify_on_release() and now both the user count is zero and
+ * the list of children is empty, send notice to user land.
+ */
+
+static void check_for_release(struct cpuset *cs)
+{
+       if (notify_on_release(cs) && atomic_read(&cs->count) == 0 &&
+           list_empty(&cs->children)) {
+               char *buf;
+
+               buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+               if (!buf)
+                       return;
+               if (cpuset_path(cs, buf, PAGE_SIZE) < 0)
+                       goto out;
+               cpuset_release_agent(buf);
+out:
+               kfree(buf);
+       }
+}
+
+/*
+ * Return in *pmask the portion of a cpusets's cpus_allowed that
+ * are online.  If none are online, walk up the cpuset hierarchy
+ * until we find one that does have some online cpus.  If we get
+ * all the way to the top and still haven't found any online cpus,
+ * return cpu_online_map.  Or if passed a NULL cs from an exit'ing
+ * task, return cpu_online_map.
+ *
+ * One way or another, we guarantee to return some non-empty subset
+ * of cpu_online_map.
+ *
+ * Call with cpuset_sem held.
+ */
+
+static void guarantee_online_cpus(const struct cpuset *cs, cpumask_t *pmask)
+{
+       while (cs && !cpus_intersects(cs->cpus_allowed, cpu_online_map))
+               cs = cs->parent;
+       if (cs)
+               cpus_and(*pmask, cs->cpus_allowed, cpu_online_map);
+       else
+               *pmask = cpu_online_map;
+       BUG_ON(!cpus_intersects(*pmask, cpu_online_map));
+}
+
+/*
+ * Return in *pmask the portion of a cpusets's mems_allowed that
+ * are online.  If none are online, walk up the cpuset hierarchy
+ * until we find one that does have some online mems.  If we get
+ * all the way to the top and still haven't found any online mems,
+ * return node_online_map.
+ *
+ * One way or another, we guarantee to return some non-empty subset
+ * of node_online_map.
+ *
+ * Call with cpuset_sem held.
+ */
+
+static void guarantee_online_mems(const struct cpuset *cs, nodemask_t *pmask)
+{
+       while (cs && !nodes_intersects(cs->mems_allowed, node_online_map))
+               cs = cs->parent;
+       if (cs)
+               nodes_and(*pmask, cs->mems_allowed, node_online_map);
+       else
+               *pmask = node_online_map;
+       BUG_ON(!nodes_intersects(*pmask, node_online_map));
+}
+
+/*
+ * Refresh current tasks mems_allowed and mems_generation from
+ * current tasks cpuset.  Call with cpuset_sem held.
+ *
+ * Be sure to call refresh_mems() on any cpuset operation which
+ * (1) holds cpuset_sem, and (2) might possibly alloc memory.
+ * Call after obtaining cpuset_sem lock, before any possible
+ * allocation.  Otherwise one risks trying to allocate memory
+ * while the task cpuset_mems_generation is not the same as
+ * the mems_generation in its cpuset, which would deadlock on
+ * cpuset_sem in cpuset_update_current_mems_allowed().
+ *
+ * Since we hold cpuset_sem, once refresh_mems() is called, the
+ * test (current->cpuset_mems_generation != cs->mems_generation)
+ * in cpuset_update_current_mems_allowed() will remain false,
+ * until we drop cpuset_sem.  Anyone else who would change our
+ * cpusets mems_generation needs to lock cpuset_sem first.
+ */
+
+static void refresh_mems(void)
+{
+       struct cpuset *cs = current->cpuset;
+
+       if (current->cpuset_mems_generation != cs->mems_generation) {
+               guarantee_online_mems(cs, &current->mems_allowed);
+               current->cpuset_mems_generation = cs->mems_generation;
+       }
+}
+
+/*
+ * is_cpuset_subset(p, q) - Is cpuset p a subset of cpuset q?
+ *
+ * One cpuset is a subset of another if all its allowed CPUs and
+ * Memory Nodes are a subset of the other, and its exclusive flags
+ * are only set if the other's are set.
+ */
+
+static int is_cpuset_subset(const struct cpuset *p, const struct cpuset *q)
+{
+       return  cpus_subset(p->cpus_allowed, q->cpus_allowed) &&
+               nodes_subset(p->mems_allowed, q->mems_allowed) &&
+               is_cpu_exclusive(p) <= is_cpu_exclusive(q) &&
+               is_mem_exclusive(p) <= is_mem_exclusive(q);
+}
+
+/*
+ * validate_change() - Used to validate that any proposed cpuset change
+ *                    follows the structural rules for cpusets.
+ *
+ * If we replaced the flag and mask values of the current cpuset
+ * (cur) with those values in the trial cpuset (trial), would
+ * our various subset and exclusive rules still be valid?  Presumes
+ * cpuset_sem held.
+ *
+ * 'cur' is the address of an actual, in-use cpuset.  Operations
+ * such as list traversal that depend on the actual address of the
+ * cpuset in the list must use cur below, not trial.
+ *
+ * 'trial' is the address of bulk structure copy of cur, with
+ * perhaps one or more of the fields cpus_allowed, mems_allowed,
+ * or flags changed to new, trial values.
+ *
+ * Return 0 if valid, -errno if not.
+ */
+
+static int validate_change(const struct cpuset *cur, const struct cpuset *trial)
+{
+       struct cpuset *c, *par;
+
+       /* Each of our child cpusets must be a subset of us */
+       list_for_each_entry(c, &cur->children, sibling) {
+               if (!is_cpuset_subset(c, trial))
+                       return -EBUSY;
+       }
+
+       /* Remaining checks don't apply to root cpuset */
+       if ((par = cur->parent) == NULL)
+               return 0;
+
+       /* We must be a subset of our parent cpuset */
+       if (!is_cpuset_subset(trial, par))
+               return -EACCES;
+
+       /* If either I or some sibling (!= me) is exclusive, we can't overlap */
+       list_for_each_entry(c, &par->children, sibling) {
+               if ((is_cpu_exclusive(trial) || is_cpu_exclusive(c)) &&
+                   c != cur &&
+                   cpus_intersects(trial->cpus_allowed, c->cpus_allowed))
+                       return -EINVAL;
+               if ((is_mem_exclusive(trial) || is_mem_exclusive(c)) &&
+                   c != cur &&
+                   nodes_intersects(trial->mems_allowed, c->mems_allowed))
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int update_cpumask(struct cpuset *cs, char *buf)
+{
+       struct cpuset trialcs;
+       int retval;
+
+       trialcs = *cs;
+       retval = cpulist_parse(buf, trialcs.cpus_allowed);
+       if (retval < 0)
+               return retval;
+       cpus_and(trialcs.cpus_allowed, trialcs.cpus_allowed, cpu_online_map);
+       if (cpus_empty(trialcs.cpus_allowed))
+               return -ENOSPC;
+       retval = validate_change(cs, &trialcs);
+       if (retval == 0)
+               cs->cpus_allowed = trialcs.cpus_allowed;
+       return retval;
+}
+
+static int update_nodemask(struct cpuset *cs, char *buf)
+{
+       struct cpuset trialcs;
+       int retval;
+
+       trialcs = *cs;
+       retval = nodelist_parse(buf, trialcs.mems_allowed);
+       if (retval < 0)
+               return retval;
+       nodes_and(trialcs.mems_allowed, trialcs.mems_allowed, node_online_map);
+       if (nodes_empty(trialcs.mems_allowed))
+               return -ENOSPC;
+       retval = validate_change(cs, &trialcs);
+       if (retval == 0) {
+               cs->mems_allowed = trialcs.mems_allowed;
+               atomic_inc(&cpuset_mems_generation);
+               cs->mems_generation = atomic_read(&cpuset_mems_generation);
+       }
+       return retval;
+}
+
+/*
+ * update_flag - read a 0 or a 1 in a file and update associated flag
+ * bit:        the bit to update (CS_CPU_EXCLUSIVE, CS_MEM_EXCLUSIVE,
+ *                                             CS_NOTIFY_ON_RELEASE)
+ * cs: the cpuset to update
+ * buf:        the buffer where we read the 0 or 1
+ */
+
+static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs, char *buf)
+{
+       int turning_on;
+       struct cpuset trialcs;
+       int err;
+
+       turning_on = (simple_strtoul(buf, NULL, 10) != 0);
+
+       trialcs = *cs;
+       if (turning_on)
+               set_bit(bit, &trialcs.flags);
+       else
+               clear_bit(bit, &trialcs.flags);
+
+       err = validate_change(cs, &trialcs);
+       if (err == 0) {
+               if (turning_on)
+                       set_bit(bit, &cs->flags);
+               else
+                       clear_bit(bit, &cs->flags);
+       }
+       return err;
+}
+
+static int attach_task(struct cpuset *cs, char *buf)
+{
+       pid_t pid;
+       struct task_struct *tsk;
+       struct cpuset *oldcs;
+       cpumask_t cpus;
+
+       if (sscanf(buf, "%d", &pid) != 1)
+               return -EIO;
+       if (cpus_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed))
+               return -ENOSPC;
+
+       if (pid) {
+               read_lock(&tasklist_lock);
+
+               tsk = find_task_by_pid(pid);
+               if (!tsk) {
+                       read_unlock(&tasklist_lock);
+                       return -ESRCH;
+               }
+
+               get_task_struct(tsk);
+               read_unlock(&tasklist_lock);
+
+               if ((current->euid) && (current->euid != tsk->uid)
+                   && (current->euid != tsk->suid)) {
+                       put_task_struct(tsk);
+                       return -EACCES;
+               }
+       } else {
+               tsk = current;
+               get_task_struct(tsk);
+       }
+
+       task_lock(tsk);
+       oldcs = tsk->cpuset;
+       if (!oldcs) {
+               task_unlock(tsk);
+               put_task_struct(tsk);
+               return -ESRCH;
+       }
+       atomic_inc(&cs->count);
+       tsk->cpuset = cs;
+       task_unlock(tsk);
+
+       guarantee_online_cpus(cs, &cpus);
+       set_cpus_allowed(tsk, cpus);
+
+       put_task_struct(tsk);
+       if (atomic_dec_and_test(&oldcs->count))
+               check_for_release(oldcs);
+       return 0;
+}
+
+/* The various types of files and directories in a cpuset file system */
+
+typedef enum {
+       FILE_ROOT,
+       FILE_DIR,
+       FILE_CPULIST,
+       FILE_MEMLIST,
+       FILE_CPU_EXCLUSIVE,
+       FILE_MEM_EXCLUSIVE,
+       FILE_NOTIFY_ON_RELEASE,
+       FILE_TASKLIST,
+} cpuset_filetype_t;
+
+static ssize_t cpuset_common_file_write(struct file *file, const char __user *userbuf,
+                                       size_t nbytes, loff_t *unused_ppos)
+{
+       struct cpuset *cs = __d_cs(file->f_dentry->d_parent);
+       struct cftype *cft = __d_cft(file->f_dentry);
+       cpuset_filetype_t type = cft->private;
+       char *buffer;
+       int retval = 0;
+
+       /* Crude upper limit on largest legitimate cpulist user might write. */
+       if (nbytes > 100 + 6 * NR_CPUS)
+               return -E2BIG;
+
+       /* +1 for nul-terminator */
+       if ((buffer = kmalloc(nbytes + 1, GFP_KERNEL)) == 0)
+               return -ENOMEM;
+
+       if (copy_from_user(buffer, userbuf, nbytes)) {
+               retval = -EFAULT;
+               goto out1;
+       }
+       buffer[nbytes] = 0;     /* nul-terminate */
+
+       down(&cpuset_sem);
+
+       if (is_removed(cs)) {
+               retval = -ENODEV;
+               goto out2;
+       }
+
+       switch (type) {
+       case FILE_CPULIST:
+               retval = update_cpumask(cs, buffer);
+               break;
+       case FILE_MEMLIST:
+               retval = update_nodemask(cs, buffer);
+               break;
+       case FILE_CPU_EXCLUSIVE:
+               retval = update_flag(CS_CPU_EXCLUSIVE, cs, buffer);
+               break;
+       case FILE_MEM_EXCLUSIVE:
+               retval = update_flag(CS_MEM_EXCLUSIVE, cs, buffer);
+               break;
+       case FILE_NOTIFY_ON_RELEASE:
+               retval = update_flag(CS_NOTIFY_ON_RELEASE, cs, buffer);
+               break;
+       case FILE_TASKLIST:
+               retval = attach_task(cs, buffer);
+               break;
+       default:
+               retval = -EINVAL;
+               goto out2;
+       }
+
+       if (retval == 0)
+               retval = nbytes;
+out2:
+       up(&cpuset_sem);
+out1:
+       kfree(buffer);
+       return retval;
+}
+
+static ssize_t cpuset_file_write(struct file *file, const char __user *buf,
+                                               size_t nbytes, loff_t *ppos)
+{
+       ssize_t retval = 0;
+       struct cftype *cft = __d_cft(file->f_dentry);
+       if (!cft)
+               return -ENODEV;
+
+       /* special function ? */
+       if (cft->write)
+               retval = cft->write(file, buf, nbytes, ppos);
+       else
+               retval = cpuset_common_file_write(file, buf, nbytes, ppos);
+
+       return retval;
+}
+
+/*
+ * These ascii lists should be read in a single call, by using a user
+ * buffer large enough to hold the entire map.  If read in smaller
+ * chunks, there is no guarantee of atomicity.  Since the display format
+ * used, list of ranges of sequential numbers, is variable length,
+ * and since these maps can change value dynamically, one could read
+ * gibberish by doing partial reads while a list was changing.
+ * A single large read to a buffer that crosses a page boundary is
+ * ok, because the result being copied to user land is not recomputed
+ * across a page fault.
+ */
+
+static int cpuset_sprintf_cpulist(char *page, struct cpuset *cs)
+{
+       cpumask_t mask;
+
+       down(&cpuset_sem);
+       mask = cs->cpus_allowed;
+       up(&cpuset_sem);
+
+       return cpulist_scnprintf(page, PAGE_SIZE, mask);
+}
+
+static int cpuset_sprintf_memlist(char *page, struct cpuset *cs)
+{
+       nodemask_t mask;
+
+       down(&cpuset_sem);
+       mask = cs->mems_allowed;
+       up(&cpuset_sem);
+
+       return nodelist_scnprintf(page, PAGE_SIZE, mask);
+}
+
+static ssize_t cpuset_common_file_read(struct file *file, char __user *buf,
+                               size_t nbytes, loff_t *ppos)
+{
+       struct cftype *cft = __d_cft(file->f_dentry);
+       struct cpuset *cs = __d_cs(file->f_dentry->d_parent);
+       cpuset_filetype_t type = cft->private;
+       char *page;
+       ssize_t retval = 0;
+       char *s;
+       char *start;
+       size_t n;
+
+       if (!(page = (char *)__get_free_page(GFP_KERNEL)))
+               return -ENOMEM;
+
+       s = page;
+
+       switch (type) {
+       case FILE_CPULIST:
+               s += cpuset_sprintf_cpulist(s, cs);
+               break;
+       case FILE_MEMLIST:
+               s += cpuset_sprintf_memlist(s, cs);
+               break;
+       case FILE_CPU_EXCLUSIVE:
+               *s++ = is_cpu_exclusive(cs) ? '1' : '0';
+               break;
+       case FILE_MEM_EXCLUSIVE:
+               *s++ = is_mem_exclusive(cs) ? '1' : '0';
+               break;
+       case FILE_NOTIFY_ON_RELEASE:
+               *s++ = notify_on_release(cs) ? '1' : '0';
+               break;
+       default:
+               retval = -EINVAL;
+               goto out;
+       }
+       *s++ = '\n';
+       *s = '\0';
+
+       start = page + *ppos;
+       n = s - start;
+       retval = n - copy_to_user(buf, start, min(n, nbytes));
+       *ppos += retval;
+out:
+       free_page((unsigned long)page);
+       return retval;
+}
+
+static ssize_t cpuset_file_read(struct file *file, char __user *buf, size_t nbytes,
+                                                               loff_t *ppos)
+{
+       ssize_t retval = 0;
+       struct cftype *cft = __d_cft(file->f_dentry);
+       if (!cft)
+               return -ENODEV;
+
+       /* special function ? */
+       if (cft->read)
+               retval = cft->read(file, buf, nbytes, ppos);
+       else
+               retval = cpuset_common_file_read(file, buf, nbytes, ppos);
+
+       return retval;
+}
+
+static int cpuset_file_open(struct inode *inode, struct file *file)
+{
+       int err;
+       struct cftype *cft;
+
+       err = generic_file_open(inode, file);
+       if (err)
+               return err;
+
+       cft = __d_cft(file->f_dentry);
+       if (!cft)
+               return -ENODEV;
+       if (cft->open)
+               err = cft->open(inode, file);
+       else
+               err = 0;
+
+       return err;
+}
+
+static int cpuset_file_release(struct inode *inode, struct file *file)
+{
+       struct cftype *cft = __d_cft(file->f_dentry);
+       if (cft->release)
+               return cft->release(inode, file);
+       return 0;
+}
+
+static struct file_operations cpuset_file_operations = {
+       .read = cpuset_file_read,
+       .write = cpuset_file_write,
+       .llseek = generic_file_llseek,
+       .open = cpuset_file_open,
+       .release = cpuset_file_release,
+};
+
+static struct inode_operations cpuset_dir_inode_operations = {
+       .lookup = simple_lookup,
+       .mkdir = cpuset_mkdir,
+       .rmdir = cpuset_rmdir,
+};
+
+static int cpuset_create_file(struct dentry *dentry, int mode)
+{
+       struct inode *inode;
+
+       if (!dentry)
+               return -ENOENT;
+       if (dentry->d_inode)
+               return -EEXIST;
+
+       inode = cpuset_new_inode(mode);
+       if (!inode)
+               return -ENOMEM;
+
+       if (S_ISDIR(mode)) {
+               inode->i_op = &cpuset_dir_inode_operations;
+               inode->i_fop = &simple_dir_operations;
+
+               /* start off with i_nlink == 2 (for "." entry) */
+               inode->i_nlink++;
+       } else if (S_ISREG(mode)) {
+               inode->i_size = 0;
+               inode->i_fop = &cpuset_file_operations;
+       }
+
+       d_instantiate(dentry, inode);
+       dget(dentry);   /* Extra count - pin the dentry in core */
+       return 0;
+}
+
+/*
+ *     cpuset_create_dir - create a directory for an object.
+ *     cs:     the cpuset we create the directory for.
+ *             It must have a valid ->parent field
+ *             And we are going to fill its ->dentry field.
+ *     name:   The name to give to the cpuset directory. Will be copied.
+ *     mode:   mode to set on new directory.
+ */
+
+static int cpuset_create_dir(struct cpuset *cs, const char *name, int mode)
+{
+       struct dentry *dentry = NULL;
+       struct dentry *parent;
+       int error = 0;
+
+       parent = cs->parent->dentry;
+       dentry = cpuset_get_dentry(parent, name);
+       if (IS_ERR(dentry))
+               return PTR_ERR(dentry);
+       error = cpuset_create_file(dentry, S_IFDIR | mode);
+       if (!error) {
+               dentry->d_fsdata = cs;
+               parent->d_inode->i_nlink++;
+               cs->dentry = dentry;
+       }
+       dput(dentry);
+
+       return error;
+}
+
+static int cpuset_add_file(struct dentry *dir, const struct cftype *cft)
+{
+       struct dentry *dentry;
+       int error;
+
+       down(&dir->d_inode->i_sem);
+       dentry = cpuset_get_dentry(dir, cft->name);
+       if (!IS_ERR(dentry)) {
+               error = cpuset_create_file(dentry, 0644 | S_IFREG);
+               if (!error)
+                       dentry->d_fsdata = (void *)cft;
+               dput(dentry);
+       } else
+               error = PTR_ERR(dentry);
+       up(&dir->d_inode->i_sem);
+       return error;
+}
+
+/*
+ * Stuff for reading the 'tasks' file.
+ *
+ * Reading this file can return large amounts of data if a cpuset has
+ * *lots* of attached tasks. So it may need several calls to read(),
+ * but we cannot guarantee that the information we produce is correct
+ * unless we produce it entirely atomically.
+ *
+ * Upon tasks file open(), a struct ctr_struct is allocated, that
+ * will have a pointer to an array (also allocated here).  The struct
+ * ctr_struct * is stored in file->private_data.  Its resources will
+ * be freed by release() when the file is closed.  The array is used
+ * to sprintf the PIDs and then used by read().
+ */
+
+/* cpusets_tasks_read array */
+
+struct ctr_struct {
+       char *buf;
+       int bufsz;
+};
+
+/*
+ * Load into 'pidarray' up to 'npids' of the tasks using cpuset 'cs'.
+ * Return actual number of pids loaded.
+ */
+static inline int pid_array_load(pid_t *pidarray, int npids, struct cpuset *cs)
+{
+       int n = 0;
+       struct task_struct *g, *p;
+
+       read_lock(&tasklist_lock);
+
+       do_each_thread(g, p) {
+               if (p->cpuset == cs) {
+                       pidarray[n++] = p->pid;
+                       if (unlikely(n == npids))
+                               goto array_full;
+               }
+       } while_each_thread(g, p);
+
+array_full:
+       read_unlock(&tasklist_lock);
+       return n;
+}
+
+static int cmppid(const void *a, const void *b)
+{
+       return *(pid_t *)a - *(pid_t *)b;
+}
+
+/*
+ * Convert array 'a' of 'npids' pid_t's to a string of newline separated
+ * decimal pids in 'buf'.  Don't write more than 'sz' chars, but return
+ * count 'cnt' of how many chars would be written if buf were large enough.
+ */
+static int pid_array_to_buf(char *buf, int sz, pid_t *a, int npids)
+{
+       int cnt = 0;
+       int i;
+
+       for (i = 0; i < npids; i++)
+               cnt += snprintf(buf + cnt, max(sz - cnt, 0), "%d\n", a[i]);
+       return cnt;
+}
+
+static int cpuset_tasks_open(struct inode *unused, struct file *file)
+{
+       struct cpuset *cs = __d_cs(file->f_dentry->d_parent);
+       struct ctr_struct *ctr;
+       pid_t *pidarray;
+       int npids;
+       char c;
+
+       if (!(file->f_mode & FMODE_READ))
+               return 0;
+
+       ctr = kmalloc(sizeof(*ctr), GFP_KERNEL);
+       if (!ctr)
+               goto err0;
+
+       /*
+        * If cpuset gets more users after we read count, we won't have
+        * enough space - tough.  This race is indistinguishable to the
+        * caller from the case that the additional cpuset users didn't
+        * show up until sometime later on.
+        */
+       npids = atomic_read(&cs->count);
+       pidarray = kmalloc(npids * sizeof(pid_t), GFP_KERNEL);
+       if (!pidarray)
+               goto err1;
+
+       npids = pid_array_load(pidarray, npids, cs);
+       sort(pidarray, npids, sizeof(pid_t), cmppid, NULL);
+
+       /* Call pid_array_to_buf() twice, first just to get bufsz */
+       ctr->bufsz = pid_array_to_buf(&c, sizeof(c), pidarray, npids) + 1;
+       ctr->buf = kmalloc(ctr->bufsz, GFP_KERNEL);
+       if (!ctr->buf)
+               goto err2;
+       ctr->bufsz = pid_array_to_buf(ctr->buf, ctr->bufsz, pidarray, npids);
+
+       kfree(pidarray);
+       file->private_data = ctr;
+       return 0;
+
+err2:
+       kfree(pidarray);
+err1:
+       kfree(ctr);
+err0:
+       return -ENOMEM;
+}
+
+static ssize_t cpuset_tasks_read(struct file *file, char __user *buf,
+                                               size_t nbytes, loff_t *ppos)
+{
+       struct ctr_struct *ctr = file->private_data;
+
+       if (*ppos + nbytes > ctr->bufsz)
+               nbytes = ctr->bufsz - *ppos;
+       if (copy_to_user(buf, ctr->buf + *ppos, nbytes))
+               return -EFAULT;
+       *ppos += nbytes;
+       return nbytes;
+}
+
+static int cpuset_tasks_release(struct inode *unused_inode, struct file *file)
+{
+       struct ctr_struct *ctr;
+
+       if (file->f_mode & FMODE_READ) {
+               ctr = file->private_data;
+               kfree(ctr->buf);
+               kfree(ctr);
+       }
+       return 0;
+}
+
+/*
+ * for the common functions, 'private' gives the type of file
+ */
+
+static struct cftype cft_tasks = {
+       .name = "tasks",
+       .open = cpuset_tasks_open,
+       .read = cpuset_tasks_read,
+       .release = cpuset_tasks_release,
+       .private = FILE_TASKLIST,
+};
+
+static struct cftype cft_cpus = {
+       .name = "cpus",
+       .private = FILE_CPULIST,
+};
+
+static struct cftype cft_mems = {
+       .name = "mems",
+       .private = FILE_MEMLIST,
+};
+
+static struct cftype cft_cpu_exclusive = {
+       .name = "cpu_exclusive",
+       .private = FILE_CPU_EXCLUSIVE,
+};
+
+static struct cftype cft_mem_exclusive = {
+       .name = "mem_exclusive",
+       .private = FILE_MEM_EXCLUSIVE,
+};
+
+static struct cftype cft_notify_on_release = {
+       .name = "notify_on_release",
+       .private = FILE_NOTIFY_ON_RELEASE,
+};
+
+static int cpuset_populate_dir(struct dentry *cs_dentry)
+{
+       int err;
+
+       if ((err = cpuset_add_file(cs_dentry, &cft_cpus)) < 0)
+               return err;
+       if ((err = cpuset_add_file(cs_dentry, &cft_mems)) < 0)
+               return err;
+       if ((err = cpuset_add_file(cs_dentry, &cft_cpu_exclusive)) < 0)
+               return err;
+       if ((err = cpuset_add_file(cs_dentry, &cft_mem_exclusive)) < 0)
+               return err;
+       if ((err = cpuset_add_file(cs_dentry, &cft_notify_on_release)) < 0)
+               return err;
+       if ((err = cpuset_add_file(cs_dentry, &cft_tasks)) < 0)
+               return err;
+       return 0;
+}
+
+/*
+ *     cpuset_create - create a cpuset
+ *     parent: cpuset that will be parent of the new cpuset.
+ *     name:           name of the new cpuset. Will be strcpy'ed.
+ *     mode:           mode to set on new inode
+ *
+ *     Must be called with the semaphore on the parent inode held
+ */
+
+static long cpuset_create(struct cpuset *parent, const char *name, int mode)
+{
+       struct cpuset *cs;
+       int err;
+
+       cs = kmalloc(sizeof(*cs), GFP_KERNEL);
+       if (!cs)
+               return -ENOMEM;
+
+       down(&cpuset_sem);
+       refresh_mems();
+       cs->flags = 0;
+       if (notify_on_release(parent))
+               set_bit(CS_NOTIFY_ON_RELEASE, &cs->flags);
+       cs->cpus_allowed = CPU_MASK_NONE;
+       cs->mems_allowed = NODE_MASK_NONE;
+       atomic_set(&cs->count, 0);
+       INIT_LIST_HEAD(&cs->sibling);
+       INIT_LIST_HEAD(&cs->children);
+       atomic_inc(&cpuset_mems_generation);
+       cs->mems_generation = atomic_read(&cpuset_mems_generation);
+
+       cs->parent = parent;
+
+       list_add(&cs->sibling, &cs->parent->children);
+
+       err = cpuset_create_dir(cs, name, mode);
+       if (err < 0)
+               goto err;
+
+       /*
+        * Release cpuset_sem before cpuset_populate_dir() because it
+        * will down() this new directory's i_sem and if we race with
+        * another mkdir, we might deadlock.
+        */
+       up(&cpuset_sem);
+
+       err = cpuset_populate_dir(cs->dentry);
+       /* If err < 0, we have a half-filled directory - oh well ;) */
+       return 0;
+err:
+       list_del(&cs->sibling);
+       up(&cpuset_sem);
+       kfree(cs);
+       return err;
+}
+
+static int cpuset_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+       struct cpuset *c_parent = dentry->d_parent->d_fsdata;
+
+       /* the vfs holds inode->i_sem already */
+       return cpuset_create(c_parent, dentry->d_name.name, mode | S_IFDIR);
+}
+
+static int cpuset_rmdir(struct inode *unused_dir, struct dentry *dentry)
+{
+       struct cpuset *cs = dentry->d_fsdata;
+       struct dentry *d;
+       struct cpuset *parent;
+
+       /* the vfs holds both inode->i_sem already */
+
+       down(&cpuset_sem);
+       refresh_mems();
+       if (atomic_read(&cs->count) > 0) {
+               up(&cpuset_sem);
+               return -EBUSY;
+       }
+       if (!list_empty(&cs->children)) {
+               up(&cpuset_sem);
+               return -EBUSY;
+       }
+       spin_lock(&cs->dentry->d_lock);
+       parent = cs->parent;
+       set_bit(CS_REMOVED, &cs->flags);
+       list_del(&cs->sibling); /* delete my sibling from parent->children */
+       if (list_empty(&parent->children))
+               check_for_release(parent);
+       d = dget(cs->dentry);
+       cs->dentry = NULL;
+       spin_unlock(&d->d_lock);
+       cpuset_d_remove_dir(d);
+       dput(d);
+       up(&cpuset_sem);
+       return 0;
+}
+
+/**
+ * cpuset_init - initialize cpusets at system boot
+ *
+ * Description: Initialize top_cpuset and the cpuset internal file system,
+ **/
+
+int __init cpuset_init(void)
+{
+       struct dentry *root;
+       int err;
+
+       top_cpuset.cpus_allowed = CPU_MASK_ALL;
+       top_cpuset.mems_allowed = NODE_MASK_ALL;
+
+       atomic_inc(&cpuset_mems_generation);
+       top_cpuset.mems_generation = atomic_read(&cpuset_mems_generation);
+
+       init_task.cpuset = &top_cpuset;
+
+       err = register_filesystem(&cpuset_fs_type);
+       if (err < 0)
+               goto out;
+       cpuset_mount = kern_mount(&cpuset_fs_type);
+       if (IS_ERR(cpuset_mount)) {
+               printk(KERN_ERR "cpuset: could not mount!\n");
+               err = PTR_ERR(cpuset_mount);
+               cpuset_mount = NULL;
+               goto out;
+       }
+       root = cpuset_mount->mnt_sb->s_root;
+       root->d_fsdata = &top_cpuset;
+       root->d_inode->i_nlink++;
+       top_cpuset.dentry = root;
+       root->d_inode->i_op = &cpuset_dir_inode_operations;
+       err = cpuset_populate_dir(root);
+out:
+       return err;
+}
+
+/**
+ * cpuset_init_smp - initialize cpus_allowed
+ *
+ * Description: Finish top cpuset after cpu, node maps are initialized
+ **/
+
+void __init cpuset_init_smp(void)
+{
+       top_cpuset.cpus_allowed = cpu_online_map;
+       top_cpuset.mems_allowed = node_online_map;
+}
+
+/**
+ * cpuset_fork - attach newly forked task to its parents cpuset.
+ * @p: pointer to task_struct of forking parent process.
+ *
+ * Description: By default, on fork, a task inherits its
+ * parents cpuset.  The pointer to the shared cpuset is
+ * automatically copied in fork.c by dup_task_struct().
+ * This cpuset_fork() routine need only increment the usage
+ * counter in that cpuset.
+ **/
+
+void cpuset_fork(struct task_struct *tsk)
+{
+       atomic_inc(&tsk->cpuset->count);
+}
+
+/**
+ * cpuset_exit - detach cpuset from exiting task
+ * @tsk: pointer to task_struct of exiting process
+ *
+ * Description: Detach cpuset from @tsk and release it.
+ *
+ * Note that cpusets marked notify_on_release force every task
+ * in them to take the global cpuset_sem semaphore when exiting.
+ * This could impact scaling on very large systems.  Be reluctant
+ * to use notify_on_release cpusets where very high task exit
+ * scaling is required on large systems.
+ *
+ * Don't even think about derefencing 'cs' after the cpuset use
+ * count goes to zero, except inside a critical section guarded
+ * by the cpuset_sem semaphore.  If you don't hold cpuset_sem,
+ * then a zero cpuset use count is a license to any other task to
+ * nuke the cpuset immediately.
+ *
+ **/
+
+void cpuset_exit(struct task_struct *tsk)
+{
+       struct cpuset *cs;
+
+       task_lock(tsk);
+       cs = tsk->cpuset;
+       tsk->cpuset = NULL;
+       task_unlock(tsk);
+
+       if (notify_on_release(cs)) {
+               down(&cpuset_sem);
+               if (atomic_dec_and_test(&cs->count))
+                       check_for_release(cs);
+               up(&cpuset_sem);
+       } else {
+               atomic_dec(&cs->count);
+       }
+}
+
+/**
+ * cpuset_cpus_allowed - return cpus_allowed mask from a tasks cpuset.
+ * @tsk: pointer to task_struct from which to obtain cpuset->cpus_allowed.
+ *
+ * Description: Returns the cpumask_t cpus_allowed of the cpuset
+ * attached to the specified @tsk.  Guaranteed to return some non-empty
+ * subset of cpu_online_map, even if this means going outside the
+ * tasks cpuset.
+ **/
+
+cpumask_t cpuset_cpus_allowed(const struct task_struct *tsk)
+{
+       cpumask_t mask;
+
+       down(&cpuset_sem);
+       task_lock((struct task_struct *)tsk);
+       guarantee_online_cpus(tsk->cpuset, &mask);
+       task_unlock((struct task_struct *)tsk);
+       up(&cpuset_sem);
+
+       return mask;
+}
+
+void cpuset_init_current_mems_allowed(void)
+{
+       current->mems_allowed = NODE_MASK_ALL;
+}
+
+/*
+ * If the current tasks cpusets mems_allowed changed behind our backs,
+ * update current->mems_allowed and mems_generation to the new value.
+ * Do not call this routine if in_interrupt().
+ */
+
+void cpuset_update_current_mems_allowed(void)
+{
+       struct cpuset *cs = current->cpuset;
+
+       if (!cs)
+               return;         /* task is exiting */
+       if (current->cpuset_mems_generation != cs->mems_generation) {
+               down(&cpuset_sem);
+               refresh_mems();
+               up(&cpuset_sem);
+       }
+}
+
+void cpuset_restrict_to_mems_allowed(unsigned long *nodes)
+{
+       bitmap_and(nodes, nodes, nodes_addr(current->mems_allowed),
+                                                       MAX_NUMNODES);
+}
+
+/*
+ * Are any of the nodes on zonelist zl allowed in current->mems_allowed?
+ */
+int cpuset_zonelist_valid_mems_allowed(struct zonelist *zl)
+{
+       int i;
+
+       for (i = 0; zl->zones[i]; i++) {
+               int nid = zl->zones[i]->zone_pgdat->node_id;
+
+               if (node_isset(nid, current->mems_allowed))
+                       return 1;
+       }
+       return 0;
+}
+
+/*
+ * Is 'current' valid, and is zone z allowed in current->mems_allowed?
+ */
+int cpuset_zone_allowed(struct zone *z)
+{
+       return in_interrupt() ||
+               node_isset(z->zone_pgdat->node_id, current->mems_allowed);
+}
+
+/*
+ * proc_cpuset_show()
+ *  - Print tasks cpuset path into seq_file.
+ *  - Used for /proc/<pid>/cpuset.
+ */
+
+static int proc_cpuset_show(struct seq_file *m, void *v)
+{
+       struct cpuset *cs;
+       struct task_struct *tsk;
+       char *buf;
+       int retval = 0;
+
+       buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       tsk = m->private;
+       down(&cpuset_sem);
+       task_lock(tsk);
+       cs = tsk->cpuset;
+       task_unlock(tsk);
+       if (!cs) {
+               retval = -EINVAL;
+               goto out;
+       }
+
+       retval = cpuset_path(cs, buf, PAGE_SIZE);
+       if (retval < 0)
+               goto out;
+       seq_puts(m, buf);
+       seq_putc(m, '\n');
+out:
+       up(&cpuset_sem);
+       kfree(buf);
+       return retval;
+}
+
+static int cpuset_open(struct inode *inode, struct file *file)
+{
+       struct task_struct *tsk = PROC_I(inode)->task;
+       return single_open(file, proc_cpuset_show, tsk);
+}
+
+struct file_operations proc_cpuset_operations = {
+       .open           = cpuset_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+/* Display task cpus_allowed, mems_allowed in /proc/<pid>/status file. */
+char *cpuset_task_status_allowed(struct task_struct *task, char *buffer)
+{
+       buffer += sprintf(buffer, "Cpus_allowed:\t");
+       buffer += cpumask_scnprintf(buffer, PAGE_SIZE, task->cpus_allowed);
+       buffer += sprintf(buffer, "\n");
+       buffer += sprintf(buffer, "Mems_allowed:\t");
+       buffer += nodemask_scnprintf(buffer, PAGE_SIZE, task->mems_allowed);
+       buffer += sprintf(buffer, "\n");
+       return buffer;
+}
diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c
new file mode 100644 (file)
index 0000000..ad85d3f
--- /dev/null
@@ -0,0 +1,1559 @@
+/*
+ * Implement CPU time clocks for the POSIX clock interface.
+ */
+
+#include <linux/sched.h>
+#include <linux/posix-timers.h>
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+
+static int check_clock(clockid_t which_clock)
+{
+       int error = 0;
+       struct task_struct *p;
+       const pid_t pid = CPUCLOCK_PID(which_clock);
+
+       if (CPUCLOCK_WHICH(which_clock) >= CPUCLOCK_MAX)
+               return -EINVAL;
+
+       if (pid == 0)
+               return 0;
+
+       read_lock(&tasklist_lock);
+       p = find_task_by_pid(pid);
+       if (!p || (CPUCLOCK_PERTHREAD(which_clock) ?
+                  p->tgid != current->tgid : p->tgid != pid)) {
+               error = -EINVAL;
+       }
+       read_unlock(&tasklist_lock);
+
+       return error;
+}
+
+static inline union cpu_time_count
+timespec_to_sample(clockid_t which_clock, const struct timespec *tp)
+{
+       union cpu_time_count ret;
+       ret.sched = 0;          /* high half always zero when .cpu used */
+       if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED) {
+               ret.sched = tp->tv_sec * NSEC_PER_SEC + tp->tv_nsec;
+       } else {
+               ret.cpu = timespec_to_cputime(tp);
+       }
+       return ret;
+}
+
+static void sample_to_timespec(clockid_t which_clock,
+                              union cpu_time_count cpu,
+                              struct timespec *tp)
+{
+       if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED) {
+               tp->tv_sec = div_long_long_rem(cpu.sched,
+                                              NSEC_PER_SEC, &tp->tv_nsec);
+       } else {
+               cputime_to_timespec(cpu.cpu, tp);
+       }
+}
+
+static inline int cpu_time_before(clockid_t which_clock,
+                                 union cpu_time_count now,
+                                 union cpu_time_count then)
+{
+       if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED) {
+               return now.sched < then.sched;
+       }  else {
+               return cputime_lt(now.cpu, then.cpu);
+       }
+}
+static inline void cpu_time_add(clockid_t which_clock,
+                               union cpu_time_count *acc,
+                               union cpu_time_count val)
+{
+       if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED) {
+               acc->sched += val.sched;
+       }  else {
+               acc->cpu = cputime_add(acc->cpu, val.cpu);
+       }
+}
+static inline union cpu_time_count cpu_time_sub(clockid_t which_clock,
+                                               union cpu_time_count a,
+                                               union cpu_time_count b)
+{
+       if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED) {
+               a.sched -= b.sched;
+       }  else {
+               a.cpu = cputime_sub(a.cpu, b.cpu);
+       }
+       return a;
+}
+
+/*
+ * Update expiry time from increment, and increase overrun count,
+ * given the current clock sample.
+ */
+static inline void bump_cpu_timer(struct k_itimer *timer,
+                                 union cpu_time_count now)
+{
+       int i;
+
+       if (timer->it.cpu.incr.sched == 0)
+               return;
+
+       if (CPUCLOCK_WHICH(timer->it_clock) == CPUCLOCK_SCHED) {
+               unsigned long long delta, incr;
+
+               if (now.sched < timer->it.cpu.expires.sched)
+                       return;
+               incr = timer->it.cpu.incr.sched;
+               delta = now.sched + incr - timer->it.cpu.expires.sched;
+               /* Don't use (incr*2 < delta), incr*2 might overflow. */
+               for (i = 0; incr < delta - incr; i++)
+                       incr = incr << 1;
+               for (; i >= 0; incr >>= 1, i--) {
+                       if (delta <= incr)
+                               continue;
+                       timer->it.cpu.expires.sched += incr;
+                       timer->it_overrun += 1 << i;
+                       delta -= incr;
+               }
+       } else {
+               cputime_t delta, incr;
+
+               if (cputime_lt(now.cpu, timer->it.cpu.expires.cpu))
+                       return;
+               incr = timer->it.cpu.incr.cpu;
+               delta = cputime_sub(cputime_add(now.cpu, incr),
+                                   timer->it.cpu.expires.cpu);
+               /* Don't use (incr*2 < delta), incr*2 might overflow. */
+               for (i = 0; cputime_lt(incr, cputime_sub(delta, incr)); i++)
+                            incr = cputime_add(incr, incr);
+               for (; i >= 0; incr = cputime_halve(incr), i--) {
+                       if (cputime_le(delta, incr))
+                               continue;
+                       timer->it.cpu.expires.cpu =
+                               cputime_add(timer->it.cpu.expires.cpu, incr);
+                       timer->it_overrun += 1 << i;
+                       delta = cputime_sub(delta, incr);
+               }
+       }
+}
+
+static inline cputime_t prof_ticks(struct task_struct *p)
+{
+       return cputime_add(p->utime, p->stime);
+}
+static inline cputime_t virt_ticks(struct task_struct *p)
+{
+       return p->utime;
+}
+static inline unsigned long long sched_ns(struct task_struct *p)
+{
+       return (p == current) ? current_sched_time(p) : p->sched_time;
+}
+
+int posix_cpu_clock_getres(clockid_t which_clock, struct timespec *tp)
+{
+       int error = check_clock(which_clock);
+       if (!error) {
+               tp->tv_sec = 0;
+               tp->tv_nsec = ((NSEC_PER_SEC + HZ - 1) / HZ);
+               if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED) {
+                       /*
+                        * If sched_clock is using a cycle counter, we
+                        * don't have any idea of its true resolution
+                        * exported, but it is much more than 1s/HZ.
+                        */
+                       tp->tv_nsec = 1;
+               }
+       }
+       return error;
+}
+
+int posix_cpu_clock_set(clockid_t which_clock, const struct timespec *tp)
+{
+       /*
+        * You can never reset a CPU clock, but we check for other errors
+        * in the call before failing with EPERM.
+        */
+       int error = check_clock(which_clock);
+       if (error == 0) {
+               error = -EPERM;
+       }
+       return error;
+}
+
+
+/*
+ * Sample a per-thread clock for the given task.
+ */
+static int cpu_clock_sample(clockid_t which_clock, struct task_struct *p,
+                           union cpu_time_count *cpu)
+{
+       switch (CPUCLOCK_WHICH(which_clock)) {
+       default:
+               return -EINVAL;
+       case CPUCLOCK_PROF:
+               cpu->cpu = prof_ticks(p);
+               break;
+       case CPUCLOCK_VIRT:
+               cpu->cpu = virt_ticks(p);
+               break;
+       case CPUCLOCK_SCHED:
+               cpu->sched = sched_ns(p);
+               break;
+       }
+       return 0;
+}
+
+/*
+ * Sample a process (thread group) clock for the given group_leader task.
+ * Must be called with tasklist_lock held for reading.
+ * Must be called with tasklist_lock held for reading, and p->sighand->siglock.
+ */
+static int cpu_clock_sample_group_locked(unsigned int clock_idx,
+                                        struct task_struct *p,
+                                        union cpu_time_count *cpu)
+{
+       struct task_struct *t = p;
+       switch (clock_idx) {
+       default:
+               return -EINVAL;
+       case CPUCLOCK_PROF:
+               cpu->cpu = cputime_add(p->signal->utime, p->signal->stime);
+               do {
+                       cpu->cpu = cputime_add(cpu->cpu, prof_ticks(t));
+                       t = next_thread(t);
+               } while (t != p);
+               break;
+       case CPUCLOCK_VIRT:
+               cpu->cpu = p->signal->utime;
+               do {
+                       cpu->cpu = cputime_add(cpu->cpu, virt_ticks(t));
+                       t = next_thread(t);
+               } while (t != p);
+               break;
+       case CPUCLOCK_SCHED:
+               cpu->sched = p->signal->sched_time;
+               /* Add in each other live thread.  */
+               while ((t = next_thread(t)) != p) {
+                       cpu->sched += t->sched_time;
+               }
+               if (p->tgid == current->tgid) {
+                       /*
+                        * We're sampling ourselves, so include the
+                        * cycles not yet banked.  We still omit
+                        * other threads running on other CPUs,
+                        * so the total can always be behind as
+                        * much as max(nthreads-1,ncpus) * (NSEC_PER_SEC/HZ).
+                        */
+                       cpu->sched += current_sched_time(current);
+               } else {
+                       cpu->sched += p->sched_time;
+               }
+               break;
+       }
+       return 0;
+}
+
+/*
+ * Sample a process (thread group) clock for the given group_leader task.
+ * Must be called with tasklist_lock held for reading.
+ */
+static int cpu_clock_sample_group(clockid_t which_clock,
+                                 struct task_struct *p,
+                                 union cpu_time_count *cpu)
+{
+       int ret;
+       unsigned long flags;
+       spin_lock_irqsave(&p->sighand->siglock, flags);
+       ret = cpu_clock_sample_group_locked(CPUCLOCK_WHICH(which_clock), p,
+                                           cpu);
+       spin_unlock_irqrestore(&p->sighand->siglock, flags);
+       return ret;
+}
+
+
+int posix_cpu_clock_get(clockid_t which_clock, struct timespec *tp)
+{
+       const pid_t pid = CPUCLOCK_PID(which_clock);
+       int error = -EINVAL;
+       union cpu_time_count rtn;
+
+       if (pid == 0) {
+               /*
+                * Special case constant value for our own clocks.
+                * We don't have to do any lookup to find ourselves.
+                */
+               if (CPUCLOCK_PERTHREAD(which_clock)) {
+                       /*
+                        * Sampling just ourselves we can do with no locking.
+                        */
+                       error = cpu_clock_sample(which_clock,
+                                                current, &rtn);
+               } else {
+                       read_lock(&tasklist_lock);
+                       error = cpu_clock_sample_group(which_clock,
+                                                      current, &rtn);
+                       read_unlock(&tasklist_lock);
+               }
+       } else {
+               /*
+                * Find the given PID, and validate that the caller
+                * should be able to see it.
+                */
+               struct task_struct *p;
+               read_lock(&tasklist_lock);
+               p = find_task_by_pid(pid);
+               if (p) {
+                       if (CPUCLOCK_PERTHREAD(which_clock)) {
+                               if (p->tgid == current->tgid) {
+                                       error = cpu_clock_sample(which_clock,
+                                                                p, &rtn);
+                               }
+                       } else if (p->tgid == pid && p->signal) {
+                               error = cpu_clock_sample_group(which_clock,
+                                                              p, &rtn);
+                       }
+               }
+               read_unlock(&tasklist_lock);
+       }
+
+       if (error)
+               return error;
+       sample_to_timespec(which_clock, rtn, tp);
+       return 0;
+}
+
+
+/*
+ * Validate the clockid_t for a new CPU-clock timer, and initialize the timer.
+ * This is called from sys_timer_create with the new timer already locked.
+ */
+int posix_cpu_timer_create(struct k_itimer *new_timer)
+{
+       int ret = 0;
+       const pid_t pid = CPUCLOCK_PID(new_timer->it_clock);
+       struct task_struct *p;
+
+       if (CPUCLOCK_WHICH(new_timer->it_clock) >= CPUCLOCK_MAX)
+               return -EINVAL;
+
+       INIT_LIST_HEAD(&new_timer->it.cpu.entry);
+       new_timer->it.cpu.incr.sched = 0;
+       new_timer->it.cpu.expires.sched = 0;
+
+       read_lock(&tasklist_lock);
+       if (CPUCLOCK_PERTHREAD(new_timer->it_clock)) {
+               if (pid == 0) {
+                       p = current;
+               } else {
+                       p = find_task_by_pid(pid);
+                       if (p && p->tgid != current->tgid)
+                               p = NULL;
+               }
+       } else {
+               if (pid == 0) {
+                       p = current->group_leader;
+               } else {
+                       p = find_task_by_pid(pid);
+                       if (p && p->tgid != pid)
+                               p = NULL;
+               }
+       }
+       new_timer->it.cpu.task = p;
+       if (p) {
+               get_task_struct(p);
+       } else {
+               ret = -EINVAL;
+       }
+       read_unlock(&tasklist_lock);
+
+       return ret;
+}
+
+/*
+ * Clean up a CPU-clock timer that is about to be destroyed.
+ * This is called from timer deletion with the timer already locked.
+ * If we return TIMER_RETRY, it's necessary to release the timer's lock
+ * and try again.  (This happens when the timer is in the middle of firing.)
+ */
+int posix_cpu_timer_del(struct k_itimer *timer)
+{
+       struct task_struct *p = timer->it.cpu.task;
+
+       if (timer->it.cpu.firing)
+               return TIMER_RETRY;
+
+       if (unlikely(p == NULL))
+               return 0;
+
+       if (!list_empty(&timer->it.cpu.entry)) {
+               read_lock(&tasklist_lock);
+               if (unlikely(p->signal == NULL)) {
+                       /*
+                        * We raced with the reaping of the task.
+                        * The deletion should have cleared us off the list.
+                        */
+                       BUG_ON(!list_empty(&timer->it.cpu.entry));
+               } else {
+                       /*
+                        * Take us off the task's timer list.
+                        */
+                       spin_lock(&p->sighand->siglock);
+                       list_del(&timer->it.cpu.entry);
+                       spin_unlock(&p->sighand->siglock);
+               }
+               read_unlock(&tasklist_lock);
+       }
+       put_task_struct(p);
+
+       return 0;
+}
+
+/*
+ * Clean out CPU timers still ticking when a thread exited.  The task
+ * pointer is cleared, and the expiry time is replaced with the residual
+ * time for later timer_gettime calls to return.
+ * This must be called with the siglock held.
+ */
+static void cleanup_timers(struct list_head *head,
+                          cputime_t utime, cputime_t stime,
+                          unsigned long long sched_time)
+{
+       struct cpu_timer_list *timer, *next;
+       cputime_t ptime = cputime_add(utime, stime);
+
+       list_for_each_entry_safe(timer, next, head, entry) {
+               timer->task = NULL;
+               list_del_init(&timer->entry);
+               if (cputime_lt(timer->expires.cpu, ptime)) {
+                       timer->expires.cpu = cputime_zero;
+               } else {
+                       timer->expires.cpu = cputime_sub(timer->expires.cpu,
+                                                        ptime);
+               }
+       }
+
+       ++head;
+       list_for_each_entry_safe(timer, next, head, entry) {
+               timer->task = NULL;
+               list_del_init(&timer->entry);
+               if (cputime_lt(timer->expires.cpu, utime)) {
+                       timer->expires.cpu = cputime_zero;
+               } else {
+                       timer->expires.cpu = cputime_sub(timer->expires.cpu,
+                                                        utime);
+               }
+       }
+
+       ++head;
+       list_for_each_entry_safe(timer, next, head, entry) {
+               timer->task = NULL;
+               list_del_init(&timer->entry);
+               if (timer->expires.sched < sched_time) {
+                       timer->expires.sched = 0;
+               } else {
+                       timer->expires.sched -= sched_time;
+               }
+       }
+}
+
+/*
+ * These are both called with the siglock held, when the current thread
+ * is being reaped.  When the final (leader) thread in the group is reaped,
+ * posix_cpu_timers_exit_group will be called after posix_cpu_timers_exit.
+ */
+void posix_cpu_timers_exit(struct task_struct *tsk)
+{
+       cleanup_timers(tsk->cpu_timers,
+                      tsk->utime, tsk->stime, tsk->sched_time);
+
+}
+void posix_cpu_timers_exit_group(struct task_struct *tsk)
+{
+       cleanup_timers(tsk->signal->cpu_timers,
+                      cputime_add(tsk->utime, tsk->signal->utime),
+                      cputime_add(tsk->stime, tsk->signal->stime),
+                      tsk->sched_time + tsk->signal->sched_time);
+}
+
+
+/*
+ * Set the expiry times of all the threads in the process so one of them
+ * will go off before the process cumulative expiry total is reached.
+ */
+static void process_timer_rebalance(struct task_struct *p,
+                                   unsigned int clock_idx,
+                                   union cpu_time_count expires,
+                                   union cpu_time_count val)
+{
+       cputime_t ticks, left;
+       unsigned long long ns, nsleft;
+       struct task_struct *t = p;
+       unsigned int nthreads = atomic_read(&p->signal->live);
+
+       switch (clock_idx) {
+       default:
+               BUG();
+               break;
+       case CPUCLOCK_PROF:
+               left = cputime_div(cputime_sub(expires.cpu, val.cpu),
+                                  nthreads);
+               do {
+                       if (!unlikely(t->exit_state)) {
+                               ticks = cputime_add(prof_ticks(t), left);
+                               if (cputime_eq(t->it_prof_expires,
+                                              cputime_zero) ||
+                                   cputime_gt(t->it_prof_expires, ticks)) {
+                                       t->it_prof_expires = ticks;
+                               }
+                       }
+                       t = next_thread(t);
+               } while (t != p);
+               break;
+       case CPUCLOCK_VIRT:
+               left = cputime_div(cputime_sub(expires.cpu, val.cpu),
+                                  nthreads);
+               do {
+                       if (!unlikely(t->exit_state)) {
+                               ticks = cputime_add(virt_ticks(t), left);
+                               if (cputime_eq(t->it_virt_expires,
+                                              cputime_zero) ||
+                                   cputime_gt(t->it_virt_expires, ticks)) {
+                                       t->it_virt_expires = ticks;
+                               }
+                       }
+                       t = next_thread(t);
+               } while (t != p);
+               break;
+       case CPUCLOCK_SCHED:
+               nsleft = expires.sched - val.sched;
+               do_div(nsleft, nthreads);
+               do {
+                       if (!unlikely(t->exit_state)) {
+                               ns = t->sched_time + nsleft;
+                               if (t->it_sched_expires == 0 ||
+                                   t->it_sched_expires > ns) {
+                                       t->it_sched_expires = ns;
+                               }
+                       }
+                       t = next_thread(t);
+               } while (t != p);
+               break;
+       }
+}
+
+static void clear_dead_task(struct k_itimer *timer, union cpu_time_count now)
+{
+       /*
+        * That's all for this thread or process.
+        * We leave our residual in expires to be reported.
+        */
+       put_task_struct(timer->it.cpu.task);
+       timer->it.cpu.task = NULL;
+       timer->it.cpu.expires = cpu_time_sub(timer->it_clock,
+                                            timer->it.cpu.expires,
+                                            now);
+}
+
+/*
+ * Insert the timer on the appropriate list before any timers that
+ * expire later.  This must be called with the tasklist_lock held
+ * for reading, and interrupts disabled.
+ */
+static void arm_timer(struct k_itimer *timer, union cpu_time_count now)
+{
+       struct task_struct *p = timer->it.cpu.task;
+       struct list_head *head, *listpos;
+       struct cpu_timer_list *const nt = &timer->it.cpu;
+       struct cpu_timer_list *next;
+       unsigned long i;
+
+       head = (CPUCLOCK_PERTHREAD(timer->it_clock) ?
+               p->cpu_timers : p->signal->cpu_timers);
+       head += CPUCLOCK_WHICH(timer->it_clock);
+
+       BUG_ON(!irqs_disabled());
+       spin_lock(&p->sighand->siglock);
+
+       listpos = head;
+       if (CPUCLOCK_WHICH(timer->it_clock) == CPUCLOCK_SCHED) {
+               list_for_each_entry(next, head, entry) {
+                       if (next->expires.sched > nt->expires.sched) {
+                               listpos = &next->entry;
+                               break;
+                       }
+               }
+       } else {
+               list_for_each_entry(next, head, entry) {
+                       if (cputime_gt(next->expires.cpu, nt->expires.cpu)) {
+                               listpos = &next->entry;
+                               break;
+                       }
+               }
+       }
+       list_add(&nt->entry, listpos);
+
+       if (listpos == head) {
+               /*
+                * We are the new earliest-expiring timer.
+                * If we are a thread timer, there can always
+                * be a process timer telling us to stop earlier.
+                */
+
+               if (CPUCLOCK_PERTHREAD(timer->it_clock)) {
+                       switch (CPUCLOCK_WHICH(timer->it_clock)) {
+                       default:
+                               BUG();
+                       case CPUCLOCK_PROF:
+                               if (cputime_eq(p->it_prof_expires,
+                                              cputime_zero) ||
+                                   cputime_gt(p->it_prof_expires,
+                                              nt->expires.cpu))
+                                       p->it_prof_expires = nt->expires.cpu;
+                               break;
+                       case CPUCLOCK_VIRT:
+                               if (cputime_eq(p->it_virt_expires,
+                                              cputime_zero) ||
+                                   cputime_gt(p->it_virt_expires,
+                                              nt->expires.cpu))
+                                       p->it_virt_expires = nt->expires.cpu;
+                               break;
+                       case CPUCLOCK_SCHED:
+                               if (p->it_sched_expires == 0 ||
+                                   p->it_sched_expires > nt->expires.sched)
+                                       p->it_sched_expires = nt->expires.sched;
+                               break;
+                       }
+               } else {
+                       /*
+                        * For a process timer, we must balance
+                        * all the live threads' expirations.
+                        */
+                       switch (CPUCLOCK_WHICH(timer->it_clock)) {
+                       default:
+                               BUG();
+                       case CPUCLOCK_VIRT:
+                               if (!cputime_eq(p->signal->it_virt_expires,
+                                               cputime_zero) &&
+                                   cputime_lt(p->signal->it_virt_expires,
+                                              timer->it.cpu.expires.cpu))
+                                       break;
+                               goto rebalance;
+                       case CPUCLOCK_PROF:
+                               if (!cputime_eq(p->signal->it_prof_expires,
+                                               cputime_zero) &&
+                                   cputime_lt(p->signal->it_prof_expires,
+                                              timer->it.cpu.expires.cpu))
+                                       break;
+                               i = p->signal->rlim[RLIMIT_CPU].rlim_cur;
+                               if (i != RLIM_INFINITY &&
+                                   i <= cputime_to_secs(timer->it.cpu.expires.cpu))
+                                       break;
+                               goto rebalance;
+                       case CPUCLOCK_SCHED:
+                       rebalance:
+                               process_timer_rebalance(
+                                       timer->it.cpu.task,
+                                       CPUCLOCK_WHICH(timer->it_clock),
+                                       timer->it.cpu.expires, now);
+                               break;
+                       }
+               }
+       }
+
+       spin_unlock(&p->sighand->siglock);
+}
+
+/*
+ * The timer is locked, fire it and arrange for its reload.
+ */
+static void cpu_timer_fire(struct k_itimer *timer)
+{
+       if (unlikely(timer->sigq == NULL)) {
+               /*
+                * This a special case for clock_nanosleep,
+                * not a normal timer from sys_timer_create.
+                */
+               wake_up_process(timer->it_process);
+               timer->it.cpu.expires.sched = 0;
+       } else if (timer->it.cpu.incr.sched == 0) {
+               /*
+                * One-shot timer.  Clear it as soon as it's fired.
+                */
+               posix_timer_event(timer, 0);
+               timer->it.cpu.expires.sched = 0;
+       } else if (posix_timer_event(timer, ++timer->it_requeue_pending)) {
+               /*
+                * The signal did not get queued because the signal
+                * was ignored, so we won't get any callback to
+                * reload the timer.  But we need to keep it
+                * ticking in case the signal is deliverable next time.
+                */
+               posix_cpu_timer_schedule(timer);
+       }
+}
+
+/*
+ * Guts of sys_timer_settime for CPU timers.
+ * This is called with the timer locked and interrupts disabled.
+ * If we return TIMER_RETRY, it's necessary to release the timer's lock
+ * and try again.  (This happens when the timer is in the middle of firing.)
+ */
+int posix_cpu_timer_set(struct k_itimer *timer, int flags,
+                       struct itimerspec *new, struct itimerspec *old)
+{
+       struct task_struct *p = timer->it.cpu.task;
+       union cpu_time_count old_expires, new_expires, val;
+       int ret;
+
+       if (unlikely(p == NULL)) {
+               /*
+                * Timer refers to a dead task's clock.
+                */
+               return -ESRCH;
+       }
+
+       new_expires = timespec_to_sample(timer->it_clock, &new->it_value);
+
+       read_lock(&tasklist_lock);
+       /*
+        * We need the tasklist_lock to protect against reaping that
+        * clears p->signal.  If p has just been reaped, we can no
+        * longer get any information about it at all.
+        */
+       if (unlikely(p->signal == NULL)) {
+               read_unlock(&tasklist_lock);
+               put_task_struct(p);
+               timer->it.cpu.task = NULL;
+               return -ESRCH;
+       }
+
+       /*
+        * Disarm any old timer after extracting its expiry time.
+        */
+       BUG_ON(!irqs_disabled());
+       spin_lock(&p->sighand->siglock);
+       old_expires = timer->it.cpu.expires;
+       list_del_init(&timer->it.cpu.entry);
+       spin_unlock(&p->sighand->siglock);
+
+       /*
+        * We need to sample the current value to convert the new
+        * value from to relative and absolute, and to convert the
+        * old value from absolute to relative.  To set a process
+        * timer, we need a sample to balance the thread expiry
+        * times (in arm_timer).  With an absolute time, we must
+        * check if it's already passed.  In short, we need a sample.
+        */
+       if (CPUCLOCK_PERTHREAD(timer->it_clock)) {
+               cpu_clock_sample(timer->it_clock, p, &val);
+       } else {
+               cpu_clock_sample_group(timer->it_clock, p, &val);
+       }
+
+       if (old) {
+               if (old_expires.sched == 0) {
+                       old->it_value.tv_sec = 0;
+                       old->it_value.tv_nsec = 0;
+               } else {
+                       /*
+                        * Update the timer in case it has
+                        * overrun already.  If it has,
+                        * we'll report it as having overrun
+                        * and with the next reloaded timer
+                        * already ticking, though we are
+                        * swallowing that pending
+                        * notification here to install the
+                        * new setting.
+                        */
+                       bump_cpu_timer(timer, val);
+                       if (cpu_time_before(timer->it_clock, val,
+                                           timer->it.cpu.expires)) {
+                               old_expires = cpu_time_sub(
+                                       timer->it_clock,
+                                       timer->it.cpu.expires, val);
+                               sample_to_timespec(timer->it_clock,
+                                                  old_expires,
+                                                  &old->it_value);
+                       } else {
+                               old->it_value.tv_nsec = 1;
+                               old->it_value.tv_sec = 0;
+                       }
+               }
+       }
+
+       if (unlikely(timer->it.cpu.firing)) {
+               /*
+                * We are colliding with the timer actually firing.
+                * Punt after filling in the timer's old value, and
+                * disable this firing since we are already reporting
+                * it as an overrun (thanks to bump_cpu_timer above).
+                */
+               read_unlock(&tasklist_lock);
+               timer->it.cpu.firing = -1;
+               ret = TIMER_RETRY;
+               goto out;
+       }
+
+       if (new_expires.sched != 0 && !(flags & TIMER_ABSTIME)) {
+               cpu_time_add(timer->it_clock, &new_expires, val);
+       }
+
+       /*
+        * Install the new expiry time (or zero).
+        * For a timer with no notification action, we don't actually
+        * arm the timer (we'll just fake it for timer_gettime).
+        */
+       timer->it.cpu.expires = new_expires;
+       if (new_expires.sched != 0 &&
+           (timer->it_sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_NONE &&
+           cpu_time_before(timer->it_clock, val, new_expires)) {
+               arm_timer(timer, val);
+       }
+
+       read_unlock(&tasklist_lock);
+
+       /*
+        * Install the new reload setting, and
+        * set up the signal and overrun bookkeeping.
+        */
+       timer->it.cpu.incr = timespec_to_sample(timer->it_clock,
+                                               &new->it_interval);
+
+       /*
+        * This acts as a modification timestamp for the timer,
+        * so any automatic reload attempt will punt on seeing
+        * that we have reset the timer manually.
+        */
+       timer->it_requeue_pending = (timer->it_requeue_pending + 2) &
+               ~REQUEUE_PENDING;
+       timer->it_overrun_last = 0;
+       timer->it_overrun = -1;
+
+       if (new_expires.sched != 0 &&
+           (timer->it_sigev_notify & ~SIGEV_THREAD_ID) != SIGEV_NONE &&
+           !cpu_time_before(timer->it_clock, val, new_expires)) {
+               /*
+                * The designated time already passed, so we notify
+                * immediately, even if the thread never runs to
+                * accumulate more time on this clock.
+                */
+               cpu_timer_fire(timer);
+       }
+
+       ret = 0;
+ out:
+       if (old) {
+               sample_to_timespec(timer->it_clock,
+                                  timer->it.cpu.incr, &old->it_interval);
+       }
+       return ret;
+}
+
+void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp)
+{
+       union cpu_time_count now;
+       struct task_struct *p = timer->it.cpu.task;
+       int clear_dead;
+
+       /*
+        * Easy part: convert the reload time.
+        */
+       sample_to_timespec(timer->it_clock,
+                          timer->it.cpu.incr, &itp->it_interval);
+
+       if (timer->it.cpu.expires.sched == 0) { /* Timer not armed at all.  */
+               itp->it_value.tv_sec = itp->it_value.tv_nsec = 0;
+               return;
+       }
+
+       if (unlikely(p == NULL)) {
+               /*
+                * This task already died and the timer will never fire.
+                * In this case, expires is actually the dead value.
+                */
+       dead:
+               sample_to_timespec(timer->it_clock, timer->it.cpu.expires,
+                                  &itp->it_value);
+               return;
+       }
+
+       /*
+        * Sample the clock to take the difference with the expiry time.
+        */
+       if (CPUCLOCK_PERTHREAD(timer->it_clock)) {
+               cpu_clock_sample(timer->it_clock, p, &now);
+               clear_dead = p->exit_state;
+       } else {
+               read_lock(&tasklist_lock);
+               if (unlikely(p->signal == NULL)) {
+                       /*
+                        * The process has been reaped.
+                        * We can't even collect a sample any more.
+                        * Call the timer disarmed, nothing else to do.
+                        */
+                       put_task_struct(p);
+                       timer->it.cpu.task = NULL;
+                       timer->it.cpu.expires.sched = 0;
+                       read_unlock(&tasklist_lock);
+                       goto dead;
+               } else {
+                       cpu_clock_sample_group(timer->it_clock, p, &now);
+                       clear_dead = (unlikely(p->exit_state) &&
+                                     thread_group_empty(p));
+               }
+               read_unlock(&tasklist_lock);
+       }
+
+       if ((timer->it_sigev_notify & ~SIGEV_THREAD_ID) == SIGEV_NONE) {
+               if (timer->it.cpu.incr.sched == 0 &&
+                   cpu_time_before(timer->it_clock,
+                                   timer->it.cpu.expires, now)) {
+                       /*
+                        * Do-nothing timer expired and has no reload,
+                        * so it's as if it was never set.
+                        */
+                       timer->it.cpu.expires.sched = 0;
+                       itp->it_value.tv_sec = itp->it_value.tv_nsec = 0;
+                       return;
+               }
+               /*
+                * Account for any expirations and reloads that should
+                * have happened.
+                */
+               bump_cpu_timer(timer, now);
+       }
+
+       if (unlikely(clear_dead)) {
+               /*
+                * We've noticed that the thread is dead, but
+                * not yet reaped.  Take this opportunity to
+                * drop our task ref.
+                */
+               clear_dead_task(timer, now);
+               goto dead;
+       }
+
+       if (cpu_time_before(timer->it_clock, now, timer->it.cpu.expires)) {
+               sample_to_timespec(timer->it_clock,
+                                  cpu_time_sub(timer->it_clock,
+                                               timer->it.cpu.expires, now),
+                                  &itp->it_value);
+       } else {
+               /*
+                * The timer should have expired already, but the firing
+                * hasn't taken place yet.  Say it's just about to expire.
+                */
+               itp->it_value.tv_nsec = 1;
+               itp->it_value.tv_sec = 0;
+       }
+}
+
+/*
+ * Check for any per-thread CPU timers that have fired and move them off
+ * the tsk->cpu_timers[N] list onto the firing list.  Here we update the
+ * tsk->it_*_expires values to reflect the remaining thread CPU timers.
+ */
+static void check_thread_timers(struct task_struct *tsk,
+                               struct list_head *firing)
+{
+       struct list_head *timers = tsk->cpu_timers;
+
+       tsk->it_prof_expires = cputime_zero;
+       while (!list_empty(timers)) {
+               struct cpu_timer_list *t = list_entry(timers->next,
+                                                     struct cpu_timer_list,
+                                                     entry);
+               if (cputime_lt(prof_ticks(tsk), t->expires.cpu)) {
+                       tsk->it_prof_expires = t->expires.cpu;
+                       break;
+               }
+               t->firing = 1;
+               list_move_tail(&t->entry, firing);
+       }
+
+       ++timers;
+       tsk->it_virt_expires = cputime_zero;
+       while (!list_empty(timers)) {
+               struct cpu_timer_list *t = list_entry(timers->next,
+                                                     struct cpu_timer_list,
+                                                     entry);
+               if (cputime_lt(virt_ticks(tsk), t->expires.cpu)) {
+                       tsk->it_virt_expires = t->expires.cpu;
+                       break;
+               }
+               t->firing = 1;
+               list_move_tail(&t->entry, firing);
+       }
+
+       ++timers;
+       tsk->it_sched_expires = 0;
+       while (!list_empty(timers)) {
+               struct cpu_timer_list *t = list_entry(timers->next,
+                                                     struct cpu_timer_list,
+                                                     entry);
+               if (tsk->sched_time < t->expires.sched) {
+                       tsk->it_sched_expires = t->expires.sched;
+                       break;
+               }
+               t->firing = 1;
+               list_move_tail(&t->entry, firing);
+       }
+}
+
+/*
+ * Check for any per-thread CPU timers that have fired and move them
+ * off the tsk->*_timers list onto the firing list.  Per-thread timers
+ * have already been taken off.
+ */
+static void check_process_timers(struct task_struct *tsk,
+                                struct list_head *firing)
+{
+       struct signal_struct *const sig = tsk->signal;
+       cputime_t utime, stime, ptime, virt_expires, prof_expires;
+       unsigned long long sched_time, sched_expires;
+       struct task_struct *t;
+       struct list_head *timers = sig->cpu_timers;
+
+       /*
+        * Don't sample the current process CPU clocks if there are no timers.
+        */
+       if (list_empty(&timers[CPUCLOCK_PROF]) &&
+           cputime_eq(sig->it_prof_expires, cputime_zero) &&
+           sig->rlim[RLIMIT_CPU].rlim_cur == RLIM_INFINITY &&
+           list_empty(&timers[CPUCLOCK_VIRT]) &&
+           cputime_eq(sig->it_virt_expires, cputime_zero) &&
+           list_empty(&timers[CPUCLOCK_SCHED]))
+               return;
+
+       /*
+        * Collect the current process totals.
+        */
+       utime = sig->utime;
+       stime = sig->stime;
+       sched_time = sig->sched_time;
+       t = tsk;
+       do {
+               utime = cputime_add(utime, t->utime);
+               stime = cputime_add(stime, t->stime);
+               sched_time += t->sched_time;
+               t = next_thread(t);
+       } while (t != tsk);
+       ptime = cputime_add(utime, stime);
+
+       prof_expires = cputime_zero;
+       while (!list_empty(timers)) {
+               struct cpu_timer_list *t = list_entry(timers->next,
+                                                     struct cpu_timer_list,
+                                                     entry);
+               if (cputime_lt(ptime, t->expires.cpu)) {
+                       prof_expires = t->expires.cpu;
+                       break;
+               }
+               t->firing = 1;
+               list_move_tail(&t->entry, firing);
+       }
+
+       ++timers;
+       virt_expires = cputime_zero;
+       while (!list_empty(timers)) {
+               struct cpu_timer_list *t = list_entry(timers->next,
+                                                     struct cpu_timer_list,
+                                                     entry);
+               if (cputime_lt(utime, t->expires.cpu)) {
+                       virt_expires = t->expires.cpu;
+                       break;
+               }
+               t->firing = 1;
+               list_move_tail(&t->entry, firing);
+       }
+
+       ++timers;
+       sched_expires = 0;
+       while (!list_empty(timers)) {
+               struct cpu_timer_list *t = list_entry(timers->next,
+                                                     struct cpu_timer_list,
+                                                     entry);
+               if (sched_time < t->expires.sched) {
+                       sched_expires = t->expires.sched;
+                       break;
+               }
+               t->firing = 1;
+               list_move_tail(&t->entry, firing);
+       }
+
+       /*
+        * Check for the special case process timers.
+        */
+       if (!cputime_eq(sig->it_prof_expires, cputime_zero)) {
+               if (cputime_ge(ptime, sig->it_prof_expires)) {
+                       /* ITIMER_PROF fires and reloads.  */
+                       sig->it_prof_expires = sig->it_prof_incr;
+                       if (!cputime_eq(sig->it_prof_expires, cputime_zero)) {
+                               sig->it_prof_expires = cputime_add(
+                                       sig->it_prof_expires, ptime);
+                       }
+                       __group_send_sig_info(SIGPROF, SEND_SIG_PRIV, tsk);
+               }
+               if (!cputime_eq(sig->it_prof_expires, cputime_zero) &&
+                   (cputime_eq(prof_expires, cputime_zero) ||
+                    cputime_lt(sig->it_prof_expires, prof_expires))) {
+                       prof_expires = sig->it_prof_expires;
+               }
+       }
+       if (!cputime_eq(sig->it_virt_expires, cputime_zero)) {
+               if (cputime_ge(utime, sig->it_virt_expires)) {
+                       /* ITIMER_VIRTUAL fires and reloads.  */
+                       sig->it_virt_expires = sig->it_virt_incr;
+                       if (!cputime_eq(sig->it_virt_expires, cputime_zero)) {
+                               sig->it_virt_expires = cputime_add(
+                                       sig->it_virt_expires, utime);
+                       }
+                       __group_send_sig_info(SIGVTALRM, SEND_SIG_PRIV, tsk);
+               }
+               if (!cputime_eq(sig->it_virt_expires, cputime_zero) &&
+                   (cputime_eq(virt_expires, cputime_zero) ||
+                    cputime_lt(sig->it_virt_expires, virt_expires))) {
+                       virt_expires = sig->it_virt_expires;
+               }
+       }
+       if (sig->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) {
+               unsigned long psecs = cputime_to_secs(ptime);
+               cputime_t x;
+               if (psecs >= sig->rlim[RLIMIT_CPU].rlim_max) {
+                       /*
+                        * At the hard limit, we just die.
+                        * No need to calculate anything else now.
+                        */
+                       __group_send_sig_info(SIGKILL, SEND_SIG_PRIV, tsk);
+                       return;
+               }
+               if (psecs >= sig->rlim[RLIMIT_CPU].rlim_cur) {
+                       /*
+                        * At the soft limit, send a SIGXCPU every second.
+                        */
+                       __group_send_sig_info(SIGXCPU, SEND_SIG_PRIV, tsk);
+                       if (sig->rlim[RLIMIT_CPU].rlim_cur
+                           < sig->rlim[RLIMIT_CPU].rlim_max) {
+                               sig->rlim[RLIMIT_CPU].rlim_cur++;
+                       }
+               }
+               x = secs_to_cputime(sig->rlim[RLIMIT_CPU].rlim_cur);
+               if (cputime_eq(prof_expires, cputime_zero) ||
+                   cputime_lt(x, prof_expires)) {
+                       prof_expires = x;
+               }
+       }
+
+       if (!cputime_eq(prof_expires, cputime_zero) ||
+           !cputime_eq(virt_expires, cputime_zero) ||
+           sched_expires != 0) {
+               /*
+                * Rebalance the threads' expiry times for the remaining
+                * process CPU timers.
+                */
+
+               cputime_t prof_left, virt_left, ticks;
+               unsigned long long sched_left, sched;
+               const unsigned int nthreads = atomic_read(&sig->live);
+
+               prof_left = cputime_sub(prof_expires, utime);
+               prof_left = cputime_sub(prof_left, stime);
+               prof_left = cputime_div(prof_left, nthreads);
+               virt_left = cputime_sub(virt_expires, utime);
+               virt_left = cputime_div(virt_left, nthreads);
+               if (sched_expires) {
+                       sched_left = sched_expires - sched_time;
+                       do_div(sched_left, nthreads);
+               } else {
+                       sched_left = 0;
+               }
+               t = tsk;
+               do {
+                       ticks = cputime_add(cputime_add(t->utime, t->stime),
+                                           prof_left);
+                       if (!cputime_eq(prof_expires, cputime_zero) &&
+                           (cputime_eq(t->it_prof_expires, cputime_zero) ||
+                            cputime_gt(t->it_prof_expires, ticks))) {
+                               t->it_prof_expires = ticks;
+                       }
+
+                       ticks = cputime_add(t->utime, virt_left);
+                       if (!cputime_eq(virt_expires, cputime_zero) &&
+                           (cputime_eq(t->it_virt_expires, cputime_zero) ||
+                            cputime_gt(t->it_virt_expires, ticks))) {
+                               t->it_virt_expires = ticks;
+                       }
+
+                       sched = t->sched_time + sched_left;
+                       if (sched_expires && (t->it_sched_expires == 0 ||
+                                             t->it_sched_expires > sched)) {
+                               t->it_sched_expires = sched;
+                       }
+
+                       do {
+                               t = next_thread(t);
+                       } while (unlikely(t->exit_state));
+               } while (t != tsk);
+       }
+}
+
+/*
+ * This is called from the signal code (via do_schedule_next_timer)
+ * when the last timer signal was delivered and we have to reload the timer.
+ */
+void posix_cpu_timer_schedule(struct k_itimer *timer)
+{
+       struct task_struct *p = timer->it.cpu.task;
+       union cpu_time_count now;
+
+       if (unlikely(p == NULL))
+               /*
+                * The task was cleaned up already, no future firings.
+                */
+               return;
+
+       /*
+        * Fetch the current sample and update the timer's expiry time.
+        */
+       if (CPUCLOCK_PERTHREAD(timer->it_clock)) {
+               cpu_clock_sample(timer->it_clock, p, &now);
+               bump_cpu_timer(timer, now);
+               if (unlikely(p->exit_state)) {
+                       clear_dead_task(timer, now);
+                       return;
+               }
+               read_lock(&tasklist_lock); /* arm_timer needs it.  */
+       } else {
+               read_lock(&tasklist_lock);
+               if (unlikely(p->signal == NULL)) {
+                       /*
+                        * The process has been reaped.
+                        * We can't even collect a sample any more.
+                        */
+                       put_task_struct(p);
+                       timer->it.cpu.task = p = NULL;
+                       timer->it.cpu.expires.sched = 0;
+                       read_unlock(&tasklist_lock);
+                       return;
+               } else if (unlikely(p->exit_state) && thread_group_empty(p)) {
+                       /*
+                        * We've noticed that the thread is dead, but
+                        * not yet reaped.  Take this opportunity to
+                        * drop our task ref.
+                        */
+                       clear_dead_task(timer, now);
+                       read_unlock(&tasklist_lock);
+                       return;
+               }
+               cpu_clock_sample_group(timer->it_clock, p, &now);
+               bump_cpu_timer(timer, now);
+               /* Leave the tasklist_lock locked for the call below.  */
+       }
+
+       /*
+        * Now re-arm for the new expiry time.
+        */
+       arm_timer(timer, now);
+
+       read_unlock(&tasklist_lock);
+}
+
+/*
+ * This is called from the timer interrupt handler.  The irq handler has
+ * already updated our counts.  We need to check if any timers fire now.
+ * Interrupts are disabled.
+ */
+void run_posix_cpu_timers(struct task_struct *tsk)
+{
+       LIST_HEAD(firing);
+       struct k_itimer *timer, *next;
+
+       BUG_ON(!irqs_disabled());
+
+#define UNEXPIRED(clock) \
+               (cputime_eq(tsk->it_##clock##_expires, cputime_zero) || \
+                cputime_lt(clock##_ticks(tsk), tsk->it_##clock##_expires))
+
+       if (UNEXPIRED(prof) && UNEXPIRED(virt) &&
+           (tsk->it_sched_expires == 0 ||
+            tsk->sched_time < tsk->it_sched_expires))
+               return;
+
+#undef UNEXPIRED
+
+       BUG_ON(tsk->exit_state);
+
+       /*
+        * Double-check with locks held.
+        */
+       read_lock(&tasklist_lock);
+       spin_lock(&tsk->sighand->siglock);
+
+       /*
+        * Here we take off tsk->cpu_timers[N] and tsk->signal->cpu_timers[N]
+        * all the timers that are firing, and put them on the firing list.
+        */
+       check_thread_timers(tsk, &firing);
+       check_process_timers(tsk, &firing);
+
+       /*
+        * We must release these locks before taking any timer's lock.
+        * There is a potential race with timer deletion here, as the
+        * siglock now protects our private firing list.  We have set
+        * the firing flag in each timer, so that a deletion attempt
+        * that gets the timer lock before we do will give it up and
+        * spin until we've taken care of that timer below.
+        */
+       spin_unlock(&tsk->sighand->siglock);
+       read_unlock(&tasklist_lock);
+
+       /*
+        * Now that all the timers on our list have the firing flag,
+        * noone will touch their list entries but us.  We'll take
+        * each timer's lock before clearing its firing flag, so no
+        * timer call will interfere.
+        */
+       list_for_each_entry_safe(timer, next, &firing, it.cpu.entry) {
+               int firing;
+               spin_lock(&timer->it_lock);
+               list_del_init(&timer->it.cpu.entry);
+               firing = timer->it.cpu.firing;
+               timer->it.cpu.firing = 0;
+               /*
+                * The firing flag is -1 if we collided with a reset
+                * of the timer, which already reported this
+                * almost-firing as an overrun.  So don't generate an event.
+                */
+               if (likely(firing >= 0)) {
+                       cpu_timer_fire(timer);
+               }
+               spin_unlock(&timer->it_lock);
+       }
+}
+
+/*
+ * Set one of the process-wide special case CPU timers.
+ * The tasklist_lock and tsk->sighand->siglock must be held by the caller.
+ * The oldval argument is null for the RLIMIT_CPU timer, where *newval is
+ * absolute; non-null for ITIMER_*, where *newval is relative and we update
+ * it to be absolute, *oldval is absolute and we update it to be relative.
+ */
+void set_process_cpu_timer(struct task_struct *tsk, unsigned int clock_idx,
+                          cputime_t *newval, cputime_t *oldval)
+{
+       union cpu_time_count now;
+       struct list_head *head;
+
+       BUG_ON(clock_idx == CPUCLOCK_SCHED);
+       cpu_clock_sample_group_locked(clock_idx, tsk, &now);
+
+       if (oldval) {
+               if (!cputime_eq(*oldval, cputime_zero)) {
+                       if (cputime_le(*oldval, now.cpu)) {
+                               /* Just about to fire. */
+                               *oldval = jiffies_to_cputime(1);
+                       } else {
+                               *oldval = cputime_sub(*oldval, now.cpu);
+                       }
+               }
+
+               if (cputime_eq(*newval, cputime_zero))
+                       return;
+               *newval = cputime_add(*newval, now.cpu);
+
+               /*
+                * If the RLIMIT_CPU timer will expire before the
+                * ITIMER_PROF timer, we have nothing else to do.
+                */
+               if (tsk->signal->rlim[RLIMIT_CPU].rlim_cur
+                   < cputime_to_secs(*newval))
+                       return;
+       }
+
+       /*
+        * Check whether there are any process timers already set to fire
+        * before this one.  If so, we don't have anything more to do.
+        */
+       head = &tsk->signal->cpu_timers[clock_idx];
+       if (list_empty(head) ||
+           cputime_ge(list_entry(head->next,
+                                 struct cpu_timer_list, entry)->expires.cpu,
+                      *newval)) {
+               /*
+                * Rejigger each thread's expiry time so that one will
+                * notice before we hit the process-cumulative expiry time.
+                */
+               union cpu_time_count expires = { .sched = 0 };
+               expires.cpu = *newval;
+               process_timer_rebalance(tsk, clock_idx, expires, now);
+       }
+}
+
+static long posix_cpu_clock_nanosleep_restart(struct restart_block *);
+
+int posix_cpu_nsleep(clockid_t which_clock, int flags,
+                    struct timespec *rqtp)
+{
+       struct restart_block *restart_block =
+           &current_thread_info()->restart_block;
+       struct k_itimer timer;
+       int error;
+
+       /*
+        * Diagnose required errors first.
+        */
+       if (CPUCLOCK_PERTHREAD(which_clock) &&
+           (CPUCLOCK_PID(which_clock) == 0 ||
+            CPUCLOCK_PID(which_clock) == current->pid))
+               return -EINVAL;
+
+       /*
+        * Set up a temporary timer and then wait for it to go off.
+        */
+       memset(&timer, 0, sizeof timer);
+       spin_lock_init(&timer.it_lock);
+       timer.it_clock = which_clock;
+       timer.it_overrun = -1;
+       error = posix_cpu_timer_create(&timer);
+       timer.it_process = current;
+       if (!error) {
+               struct timespec __user *rmtp;
+               static struct itimerspec zero_it;
+               struct itimerspec it = { .it_value = *rqtp,
+                                        .it_interval = {} };
+
+               spin_lock_irq(&timer.it_lock);
+               error = posix_cpu_timer_set(&timer, flags, &it, NULL);
+               if (error) {
+                       spin_unlock_irq(&timer.it_lock);
+                       return error;
+               }
+
+               while (!signal_pending(current)) {
+                       if (timer.it.cpu.expires.sched == 0) {
+                               /*
+                                * Our timer fired and was reset.
+                                */
+                               spin_unlock_irq(&timer.it_lock);
+                               return 0;
+                       }
+
+                       /*
+                        * Block until cpu_timer_fire (or a signal) wakes us.
+                        */
+                       __set_current_state(TASK_INTERRUPTIBLE);
+                       spin_unlock_irq(&timer.it_lock);
+                       schedule();
+                       spin_lock_irq(&timer.it_lock);
+               }
+
+               /*
+                * We were interrupted by a signal.
+                */
+               sample_to_timespec(which_clock, timer.it.cpu.expires, rqtp);
+               posix_cpu_timer_set(&timer, 0, &zero_it, &it);
+               spin_unlock_irq(&timer.it_lock);
+
+               if ((it.it_value.tv_sec | it.it_value.tv_nsec) == 0) {
+                       /*
+                        * It actually did fire already.
+                        */
+                       return 0;
+               }
+
+               /*
+                * Report back to the user the time still remaining.
+                */
+               rmtp = (struct timespec __user *) restart_block->arg1;
+               if (rmtp != NULL && !(flags & TIMER_ABSTIME) &&
+                   copy_to_user(rmtp, &it.it_value, sizeof *rmtp))
+                       return -EFAULT;
+
+               restart_block->fn = posix_cpu_clock_nanosleep_restart;
+               /* Caller already set restart_block->arg1 */
+               restart_block->arg0 = which_clock;
+               restart_block->arg2 = rqtp->tv_sec;
+               restart_block->arg3 = rqtp->tv_nsec;
+
+               error = -ERESTART_RESTARTBLOCK;
+       }
+
+       return error;
+}
+
+static long
+posix_cpu_clock_nanosleep_restart(struct restart_block *restart_block)
+{
+       clockid_t which_clock = restart_block->arg0;
+       struct timespec t = { .tv_sec = restart_block->arg2,
+                             .tv_nsec = restart_block->arg3 };
+       restart_block->fn = do_no_restart_syscall;
+       return posix_cpu_nsleep(which_clock, TIMER_ABSTIME, &t);
+}
+
+
+#define PROCESS_CLOCK  MAKE_PROCESS_CPUCLOCK(0, CPUCLOCK_SCHED)
+#define THREAD_CLOCK   MAKE_THREAD_CPUCLOCK(0, CPUCLOCK_SCHED)
+
+static int process_cpu_clock_getres(clockid_t which_clock, struct timespec *tp)
+{
+       return posix_cpu_clock_getres(PROCESS_CLOCK, tp);
+}
+static int process_cpu_clock_get(clockid_t which_clock, struct timespec *tp)
+{
+       return posix_cpu_clock_get(PROCESS_CLOCK, tp);
+}
+static int process_cpu_timer_create(struct k_itimer *timer)
+{
+       timer->it_clock = PROCESS_CLOCK;
+       return posix_cpu_timer_create(timer);
+}
+static int process_cpu_nsleep(clockid_t which_clock, int flags,
+                             struct timespec *rqtp)
+{
+       return posix_cpu_nsleep(PROCESS_CLOCK, flags, rqtp);
+}
+static int thread_cpu_clock_getres(clockid_t which_clock, struct timespec *tp)
+{
+       return posix_cpu_clock_getres(THREAD_CLOCK, tp);
+}
+static int thread_cpu_clock_get(clockid_t which_clock, struct timespec *tp)
+{
+       return posix_cpu_clock_get(THREAD_CLOCK, tp);
+}
+static int thread_cpu_timer_create(struct k_itimer *timer)
+{
+       timer->it_clock = THREAD_CLOCK;
+       return posix_cpu_timer_create(timer);
+}
+static int thread_cpu_nsleep(clockid_t which_clock, int flags,
+                             struct timespec *rqtp)
+{
+       return -EINVAL;
+}
+
+static __init int init_posix_cpu_timers(void)
+{
+       struct k_clock process = {
+               .clock_getres = process_cpu_clock_getres,
+               .clock_get = process_cpu_clock_get,
+               .clock_set = do_posix_clock_nosettime,
+               .timer_create = process_cpu_timer_create,
+               .nsleep = process_cpu_nsleep,
+       };
+       struct k_clock thread = {
+               .clock_getres = thread_cpu_clock_getres,
+               .clock_get = thread_cpu_clock_get,
+               .clock_set = do_posix_clock_nosettime,
+               .timer_create = thread_cpu_timer_create,
+               .nsleep = thread_cpu_nsleep,
+       };
+
+       register_posix_clock(CLOCK_PROCESS_CPUTIME_ID, &process);
+       register_posix_clock(CLOCK_THREAD_CPUTIME_ID, &thread);
+
+       return 0;
+}
+__initcall(init_posix_cpu_timers);
diff --git a/lib/sha1.c b/lib/sha1.c
new file mode 100644 (file)
index 0000000..2f7f114
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * SHA transform algorithm, originally taken from code written by
+ * Peter Gutmann, and placed in the public domain.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/cryptohash.h>
+
+/* The SHA f()-functions.  */
+
+#define f1(x,y,z)   (z ^ (x & (y ^ z)))                /* x ? y : z */
+#define f2(x,y,z)   (x ^ y ^ z)                        /* XOR */
+#define f3(x,y,z)   ((x & y) + (z & (x ^ y)))  /* majority */
+
+/* The SHA Mysterious Constants */
+
+#define K1  0x5A827999L                        /* Rounds  0-19: sqrt(2) * 2^30 */
+#define K2  0x6ED9EBA1L                        /* Rounds 20-39: sqrt(3) * 2^30 */
+#define K3  0x8F1BBCDCL                        /* Rounds 40-59: sqrt(5) * 2^30 */
+#define K4  0xCA62C1D6L                        /* Rounds 60-79: sqrt(10) * 2^30 */
+
+/*
+ * sha_transform: single block SHA1 transform
+ *
+ * @digest: 160 bit digest to update
+ * @data:   512 bits of data to hash
+ * @W:      80 words of workspace (see note)
+ *
+ * This function generates a SHA1 digest for a single 512-bit block.
+ * Be warned, it does not handle padding and message digest, do not
+ * confuse it with the full FIPS 180-1 digest algorithm for variable
+ * length messages.
+ *
+ * Note: If the hash is security sensitive, the caller should be sure
+ * to clear the workspace. This is left to the caller to avoid
+ * unnecessary clears between chained hashing operations.
+ */
+void sha_transform(__u32 *digest, const char *in, __u32 *W)
+{
+       __u32 a, b, c, d, e, t, i;
+
+       for (i = 0; i < 16; i++)
+               W[i] = be32_to_cpu(((const __u32 *)in)[i]);
+
+       for (i = 0; i < 64; i++)
+               W[i+16] = rol32(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 1);
+
+       a = digest[0];
+       b = digest[1];
+       c = digest[2];
+       d = digest[3];
+       e = digest[4];
+
+       for (i = 0; i < 20; i++) {
+               t = f1(b, c, d) + K1 + rol32(a, 5) + e + W[i];
+               e = d; d = c; c = rol32(b, 30); b = a; a = t;
+       }
+
+       for (; i < 40; i ++) {
+               t = f2(b, c, d) + K2 + rol32(a, 5) + e + W[i];
+               e = d; d = c; c = rol32(b, 30); b = a; a = t;
+       }
+
+       for (; i < 60; i ++) {
+               t = f3(b, c, d) + K3 + rol32(a, 5) + e + W[i];
+               e = d; d = c; c = rol32(b, 30); b = a; a = t;
+       }
+
+       for (; i < 80; i ++) {
+               t = f2(b, c, d) + K4 + rol32(a, 5) + e + W[i];
+               e = d; d = c; c = rol32(b, 30); b = a; a = t;
+       }
+
+       digest[0] += a;
+       digest[1] += b;
+       digest[2] += c;
+       digest[3] += d;
+       digest[4] += e;
+}
+EXPORT_SYMBOL(sha_transform);
+
+/*
+ * sha_init: initialize the vectors for a SHA1 digest
+ *
+ * @buf: vector to initialize
+ */
+void sha_init(__u32 *buf)
+{
+       buf[0] = 0x67452301;
+       buf[1] = 0xefcdab89;
+       buf[2] = 0x98badcfe;
+       buf[3] = 0x10325476;
+       buf[4] = 0xc3d2e1f0;
+}
+
diff --git a/lib/sort.c b/lib/sort.c
new file mode 100644 (file)
index 0000000..b73dbb0
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * A fast, small, non-recursive O(nlog n) sort for the Linux kernel
+ *
+ * Jan 23 2005  Matt Mackall <mpm@selenic.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+void u32_swap(void *a, void *b, int size)
+{
+       u32 t = *(u32 *)a;
+       *(u32 *)a = *(u32 *)b;
+       *(u32 *)b = t;
+}
+
+void generic_swap(void *a, void *b, int size)
+{
+       char t;
+
+       do {
+               t = *(char *)a;
+               *(char *)a++ = *(char *)b;
+               *(char *)b++ = t;
+       } while (--size > 0);
+}
+
+/*
+ * sort - sort an array of elements
+ * @base: pointer to data to sort
+ * @num: number of elements
+ * @size: size of each element
+ * @cmp: pointer to comparison function
+ * @swap: pointer to swap function or NULL
+ *
+ * This function does a heapsort on the given array. You may provide a
+ * swap function optimized to your element type.
+ *
+ * Sorting time is O(n log n) both on average and worst-case. While
+ * qsort is about 20% faster on average, it suffers from exploitable
+ * O(n*n) worst-case behavior and extra memory requirements that make
+ * it less suitable for kernel use.
+ */
+
+void sort(void *base, size_t num, size_t size,
+         int (*cmp)(const void *, const void *),
+         void (*swap)(void *, void *, int size))
+{
+       /* pre-scale counters for performance */
+       int i = (num/2) * size, n = num * size, c, r;
+
+       if (!swap)
+               swap = (size == 4 ? u32_swap : generic_swap);
+
+       /* heapify */
+       for ( ; i >= 0; i -= size) {
+               for (r = i; r * 2 < n; r  = c) {
+                       c = r * 2;
+                       if (c < n - size && cmp(base + c, base + c + size) < 0)
+                               c += size;
+                       if (cmp(base + r, base + c) >= 0)
+                               break;
+                       swap(base + r, base + c, size);
+               }
+       }
+
+       /* sort */
+       for (i = n - size; i >= 0; i -= size) {
+               swap(base, base + i, size);
+               for (r = 0; r * 2 < i; r = c) {
+                       c = r * 2;
+                       if (c < i - size && cmp(base + c, base + c + size) < 0)
+                               c += size;
+                       if (cmp(base + r, base + c) >= 0)
+                               break;
+                       swap(base + r, base + c, size);
+               }
+       }
+}
+
+EXPORT_SYMBOL(sort);
+
+#if 0
+/* a simple boot-time regression test */
+
+int cmpint(const void *a, const void *b)
+{
+       return *(int *)a - *(int *)b;
+}
+
+static int sort_test(void)
+{
+       int *a, i, r = 1;
+
+       a = kmalloc(1000 * sizeof(int), GFP_KERNEL);
+       BUG_ON(!a);
+
+       printk("testing sort()\n");
+
+       for (i = 0; i < 1000; i++) {
+               r = (r * 725861) % 6599;
+               a[i] = r;
+       }
+
+       sort(a, 1000, sizeof(int), cmpint, NULL);
+
+       for (i = 0; i < 999; i++)
+               if (a[i] > a[i+1]) {
+                       printk("sort() failed!\n");
+                       break;
+               }
+
+       kfree(a);
+
+       return 0;
+}
+
+module_init(sort_test);
+#endif
diff --git a/net/ipv4/multipath_drr.c b/net/ipv4/multipath_drr.c
new file mode 100644 (file)
index 0000000..c9cf872
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ *              Device round robin policy for multipath.
+ *
+ *
+ * Version:    $Id: multipath_drr.c,v 1.1.2.1 2004/09/16 07:42:34 elueck Exp $
+ *
+ * Authors:    Einar Lueck <elueck@de.ibm.com><lkml@einar-lueck.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.
+ */
+
+#include <linux/config.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/igmp.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/mroute.h>
+#include <linux/init.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/icmp.h>
+#include <net/udp.h>
+#include <net/raw.h>
+#include <linux/notifier.h>
+#include <linux/if_arp.h>
+#include <linux/netfilter_ipv4.h>
+#include <net/ipip.h>
+#include <net/checksum.h>
+#include <net/ip_mp_alg.h>
+
+struct multipath_device {
+       int             ifi; /* interface index of device */
+       atomic_t        usecount;
+       int             allocated;
+};
+
+#define MULTIPATH_MAX_DEVICECANDIDATES 10
+
+static struct multipath_device state[MULTIPATH_MAX_DEVICECANDIDATES];
+static DEFINE_SPINLOCK(state_lock);
+
+static int inline __multipath_findslot(void)
+{
+       int i;
+
+       for (i = 0; i < MULTIPATH_MAX_DEVICECANDIDATES; i++) {
+               if (state[i].allocated == 0)
+                       return i;
+       }
+       return -1;
+}
+
+static int inline __multipath_finddev(int ifindex)
+{
+       int i;
+
+       for (i = 0; i < MULTIPATH_MAX_DEVICECANDIDATES; i++) {
+               if (state[i].allocated != 0 &&
+                   state[i].ifi == ifindex)
+                       return i;
+       }
+       return -1;
+}
+
+static int drr_dev_event(struct notifier_block *this,
+                        unsigned long event, void *ptr)
+{
+       struct net_device *dev = ptr;
+       int devidx;
+
+       switch (event) {
+       case NETDEV_UNREGISTER:
+       case NETDEV_DOWN:
+               spin_lock_bh(&state_lock);
+
+               devidx = __multipath_finddev(dev->ifindex);
+               if (devidx != -1) {
+                       state[devidx].allocated = 0;
+                       state[devidx].ifi = 0;
+                       atomic_set(&state[devidx].usecount, 0);
+               }
+
+               spin_unlock_bh(&state_lock);
+               break;
+       };
+
+       return NOTIFY_DONE;
+}
+
+struct notifier_block drr_dev_notifier = {
+       .notifier_call  = drr_dev_event,
+};
+
+
+static void drr_safe_inc(atomic_t *usecount)
+{
+       int n;
+
+       atomic_inc(usecount);
+
+       n = atomic_read(usecount);
+       if (n <= 0) {
+               int i;
+
+               spin_lock_bh(&state_lock);
+
+               for (i = 0; i < MULTIPATH_MAX_DEVICECANDIDATES; i++)
+                       atomic_set(&state[i].usecount, 0);
+
+               spin_unlock_bh(&state_lock);
+       }
+}
+
+static void drr_select_route(const struct flowi *flp,
+                            struct rtable *first, struct rtable **rp)
+{
+       struct rtable *nh, *result, *cur_min;
+       int min_usecount = -1; 
+       int devidx = -1;
+       int cur_min_devidx = -1;
+
+       /* 1. make sure all alt. nexthops have the same GC related data */
+       /* 2. determine the new candidate to be returned */
+       result = NULL;
+       cur_min = NULL;
+       for (nh = rcu_dereference(first); nh;
+            nh = rcu_dereference(nh->u.rt_next)) {
+               if ((nh->u.dst.flags & DST_BALANCED) != 0 &&
+                   multipath_comparekeys(&nh->fl, flp)) {
+                       int nh_ifidx = nh->u.dst.dev->ifindex;
+
+                       nh->u.dst.lastuse = jiffies;
+                       nh->u.dst.__use++;
+                       if (result != NULL)
+                               continue;
+
+                       /* search for the output interface */
+
+                       /* this is not SMP safe, only add/remove are
+                        * SMP safe as wrong usecount updates have no big
+                        * impact
+                        */
+                       devidx = __multipath_finddev(nh_ifidx);
+                       if (devidx == -1) {
+                               /* add the interface to the array 
+                                * SMP safe
+                                */
+                               spin_lock_bh(&state_lock);
+
+                               /* due to SMP: search again */
+                               devidx = __multipath_finddev(nh_ifidx);
+                               if (devidx == -1) {
+                                       /* add entry for device */
+                                       devidx = __multipath_findslot();
+                                       if (devidx == -1) {
+                                               /* unlikely but possible */
+                                               continue;
+                                       }
+
+                                       state[devidx].allocated = 1;
+                                       state[devidx].ifi = nh_ifidx;
+                                       atomic_set(&state[devidx].usecount, 0);
+                                       min_usecount = 0;
+                               }
+
+                               spin_unlock_bh(&state_lock);
+                       }
+
+                       if (min_usecount == 0) {
+                               /* if the device has not been used it is
+                                * the primary target
+                                */
+                               drr_safe_inc(&state[devidx].usecount);
+                               result = nh;
+                       } else {
+                               int count =
+                                       atomic_read(&state[devidx].usecount);
+
+                               if (min_usecount == -1 ||
+                                   count < min_usecount) {
+                                       cur_min = nh;
+                                       cur_min_devidx = devidx;
+                                       min_usecount = count;
+                               }
+                       }
+               }
+       }
+
+       if (!result) {
+               if (cur_min) {
+                       drr_safe_inc(&state[cur_min_devidx].usecount);
+                       result = cur_min;
+               } else {
+                       result = first;
+               }
+       }
+
+       *rp = result;
+}
+
+static struct ip_mp_alg_ops drr_ops = {
+       .mp_alg_select_route    =       drr_select_route,
+};
+
+static int __init drr_init(void)
+{
+       int err = register_netdevice_notifier(&drr_dev_notifier);
+
+       if (err)
+               return err;
+
+       err = multipath_alg_register(&drr_ops, IP_MP_ALG_DRR);
+       if (err)
+               goto fail;
+
+       return 0;
+
+fail:
+       unregister_netdevice_notifier(&drr_dev_notifier);
+       return err;
+}
+
+static void __exit drr_exit(void)
+{
+       unregister_netdevice_notifier(&drr_dev_notifier);
+       multipath_alg_unregister(&drr_ops, IP_MP_ALG_DRR);
+}
+
+module_init(drr_init);
+module_exit(drr_exit);
+MODULE_LICENSE("GPL");
diff --git a/net/ipv4/multipath_random.c b/net/ipv4/multipath_random.c
new file mode 100644 (file)
index 0000000..5249dbe
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ *              Random policy for multipath.
+ *
+ *
+ * Version:    $Id: multipath_random.c,v 1.1.2.3 2004/09/21 08:42:11 elueck Exp $
+ *
+ * Authors:    Einar Lueck <elueck@de.ibm.com><lkml@einar-lueck.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.
+ */
+
+#include <linux/config.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/igmp.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/mroute.h>
+#include <linux/init.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/icmp.h>
+#include <net/udp.h>
+#include <net/raw.h>
+#include <linux/notifier.h>
+#include <linux/if_arp.h>
+#include <linux/netfilter_ipv4.h>
+#include <net/ipip.h>
+#include <net/checksum.h>
+#include <net/ip_mp_alg.h>
+
+#define MULTIPATH_MAX_CANDIDATES 40
+
+/* interface to random number generation */
+static unsigned int RANDOM_SEED = 93186752;
+
+static inline unsigned int random(unsigned int ubound)
+{
+       static unsigned int a = 1588635695,
+               q = 2,
+               r = 1117695901;
+
+       RANDOM_SEED = a*(RANDOM_SEED % q) - r*(RANDOM_SEED / q);
+
+       return RANDOM_SEED % ubound;
+}
+
+
+static void random_select_route(const struct flowi *flp,
+                               struct rtable *first,
+                               struct rtable **rp)
+{
+       struct rtable *rt;
+       struct rtable *decision;
+       unsigned char candidate_count = 0;
+
+       /* count all candidate */
+       for (rt = rcu_dereference(first); rt;
+            rt = rcu_dereference(rt->u.rt_next)) {
+               if ((rt->u.dst.flags & DST_BALANCED) != 0 &&
+                   multipath_comparekeys(&rt->fl, flp))
+                       ++candidate_count;
+       }
+
+       /* choose a random candidate */
+       decision = first;
+       if (candidate_count > 1) {
+               unsigned char i = 0;
+               unsigned char candidate_no = (unsigned char)
+                       random(candidate_count);
+
+               /* find chosen candidate and adjust GC data for all candidates
+                * to ensure they stay in cache
+                */
+               for (rt = first; rt; rt = rt->u.rt_next) {
+                       if ((rt->u.dst.flags & DST_BALANCED) != 0 &&
+                           multipath_comparekeys(&rt->fl, flp)) {
+                               rt->u.dst.lastuse = jiffies;
+
+                               if (i == candidate_no)
+                                       decision = rt;
+
+                               if (i >= candidate_count)
+                                       break;
+
+                               i++;
+                       }
+               }
+       }
+
+       decision->u.dst.__use++;
+       *rp = decision;
+}
+
+static struct ip_mp_alg_ops random_ops = {
+       .mp_alg_select_route    =       random_select_route,
+};
+
+static int __init random_init(void)
+{
+       return multipath_alg_register(&random_ops, IP_MP_ALG_RANDOM);
+}
+
+static void __exit random_exit(void)
+{
+       multipath_alg_unregister(&random_ops, IP_MP_ALG_RANDOM);
+}
+
+module_init(random_init);
+module_exit(random_exit);
+MODULE_LICENSE("GPL");
diff --git a/net/ipv4/multipath_rr.c b/net/ipv4/multipath_rr.c
new file mode 100644 (file)
index 0000000..b6cd287
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *              Round robin policy for multipath.
+ *
+ *
+ * Version:    $Id: multipath_rr.c,v 1.1.2.2 2004/09/16 07:42:34 elueck Exp $
+ *
+ * Authors:    Einar Lueck <elueck@de.ibm.com><lkml@einar-lueck.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.
+ */
+
+#include <linux/config.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/igmp.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/mroute.h>
+#include <linux/init.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/icmp.h>
+#include <net/udp.h>
+#include <net/raw.h>
+#include <linux/notifier.h>
+#include <linux/if_arp.h>
+#include <linux/netfilter_ipv4.h>
+#include <net/ipip.h>
+#include <net/checksum.h>
+#include <net/ip_mp_alg.h>
+
+static void rr_select_route(const struct flowi *flp,
+                           struct rtable *first, struct rtable **rp)
+{
+       struct rtable *nh, *result, *min_use_cand = NULL;
+       int min_use = -1;
+
+       /* 1. make sure all alt. nexthops have the same GC related data
+        * 2. determine the new candidate to be returned
+        */
+       result = NULL;
+       for (nh = rcu_dereference(first); nh;
+            nh = rcu_dereference(nh->u.rt_next)) {
+               if ((nh->u.dst.flags & DST_BALANCED) != 0 &&
+                   multipath_comparekeys(&nh->fl, flp)) {
+                       nh->u.dst.lastuse = jiffies;
+
+                       if (min_use == -1 || nh->u.dst.__use < min_use) {
+                               min_use = nh->u.dst.__use;
+                               min_use_cand = nh;
+                       }
+               }
+       }
+       result = min_use_cand;
+       if (!result)
+               result = first;
+
+       result->u.dst.__use++;
+       *rp = result;
+}
+
+static struct ip_mp_alg_ops rr_ops = {
+       .mp_alg_select_route    =       rr_select_route,
+};
+
+static int __init rr_init(void)
+{
+       return multipath_alg_register(&rr_ops, IP_MP_ALG_RR);
+}
+
+static void __exit rr_exit(void)
+{
+       multipath_alg_unregister(&rr_ops, IP_MP_ALG_RR);
+}
+
+module_init(rr_init);
+module_exit(rr_exit);
+MODULE_LICENSE("GPL");
diff --git a/net/ipv4/multipath_wrandom.c b/net/ipv4/multipath_wrandom.c
new file mode 100644 (file)
index 0000000..bd7d75b
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ *              Weighted random policy for multipath.
+ *
+ *
+ * Version:    $Id: multipath_wrandom.c,v 1.1.2.3 2004/09/22 07:51:40 elueck Exp $
+ *
+ * Authors:    Einar Lueck <elueck@de.ibm.com><lkml@einar-lueck.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.
+ */
+
+#include <linux/config.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/igmp.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/mroute.h>
+#include <linux/init.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/icmp.h>
+#include <net/udp.h>
+#include <net/raw.h>
+#include <linux/notifier.h>
+#include <linux/if_arp.h>
+#include <linux/netfilter_ipv4.h>
+#include <net/ipip.h>
+#include <net/checksum.h>
+#include <net/ip_fib.h>
+#include <net/ip_mp_alg.h>
+
+#define MULTIPATH_STATE_SIZE 15
+
+struct multipath_candidate {
+       struct multipath_candidate      *next;
+       int                             power;
+       struct rtable                   *rt;
+};
+
+struct multipath_dest {
+       struct list_head        list;
+
+       const struct fib_nh     *nh_info;
+       __u32                   netmask;
+       __u32                   network;
+       unsigned char           prefixlen;
+
+       struct rcu_head         rcu;
+};
+
+struct multipath_bucket {
+       struct list_head        head;
+       spinlock_t              lock;
+};
+
+struct multipath_route {
+       struct list_head        list;
+
+       int                     oif;
+       __u32                   gw;
+       struct list_head        dests;
+
+       struct rcu_head         rcu;
+};
+
+/* state: primarily weight per route information */
+static struct multipath_bucket state[MULTIPATH_STATE_SIZE];
+
+/* interface to random number generation */
+static unsigned int RANDOM_SEED = 93186752;
+
+static inline unsigned int random(unsigned int ubound)
+{
+       static unsigned int a = 1588635695,
+               q = 2,
+               r = 1117695901;
+       RANDOM_SEED = a*(RANDOM_SEED % q) - r*(RANDOM_SEED / q);
+       return RANDOM_SEED % ubound;
+}
+
+static unsigned char __multipath_lookup_weight(const struct flowi *fl,
+                                              const struct rtable *rt)
+{
+       const int state_idx = rt->idev->dev->ifindex % MULTIPATH_STATE_SIZE;
+       struct multipath_route *r;
+       struct multipath_route *target_route = NULL;
+       struct multipath_dest *d;
+       int weight = 1;
+
+       /* lookup the weight information for a certain route */
+       rcu_read_lock();
+
+       /* find state entry for gateway or add one if necessary */
+       list_for_each_entry_rcu(r, &state[state_idx].head, list) {
+               if (r->gw == rt->rt_gateway &&
+                   r->oif == rt->idev->dev->ifindex) {
+                       target_route = r;
+                       break;
+               }
+       }
+
+       if (!target_route) {
+               /* this should not happen... but we are prepared */
+               printk( KERN_CRIT"%s: missing state for gateway: %u and " \
+                       "device %d\n", __FUNCTION__, rt->rt_gateway,
+                       rt->idev->dev->ifindex);
+               goto out;
+       }
+
+       /* find state entry for destination */
+       list_for_each_entry_rcu(d, &target_route->dests, list) {
+               __u32 targetnetwork = fl->fl4_dst & 
+                       (0xFFFFFFFF >> (32 - d->prefixlen));
+
+               if ((targetnetwork & d->netmask) == d->network) {
+                       weight = d->nh_info->nh_weight;
+                       goto out;
+               }
+       }
+
+out:
+       rcu_read_unlock();
+       return weight;
+}
+
+static void wrandom_init_state(void) 
+{
+       int i;
+
+       for (i = 0; i < MULTIPATH_STATE_SIZE; ++i) {
+               INIT_LIST_HEAD(&state[i].head);
+               spin_lock_init(&state[i].lock);
+       }
+}
+
+static void wrandom_select_route(const struct flowi *flp,
+                                struct rtable *first,
+                                struct rtable **rp)
+{
+       struct rtable *rt;
+       struct rtable *decision;
+       struct multipath_candidate *first_mpc = NULL;
+       struct multipath_candidate *mpc, *last_mpc = NULL;
+       int power = 0;
+       int last_power;
+       int selector;
+       const size_t size_mpc = sizeof(struct multipath_candidate);
+
+       /* collect all candidates and identify their weights */
+       for (rt = rcu_dereference(first); rt;
+            rt = rcu_dereference(rt->u.rt_next)) {
+               if ((rt->u.dst.flags & DST_BALANCED) != 0 &&
+                   multipath_comparekeys(&rt->fl, flp)) {
+                       struct multipath_candidate* mpc =
+                               (struct multipath_candidate*)
+                               kmalloc(size_mpc, GFP_ATOMIC);
+
+                       if (!mpc)
+                               return;
+
+                       power += __multipath_lookup_weight(flp, rt) * 10000;
+
+                       mpc->power = power;
+                       mpc->rt = rt;
+                       mpc->next = NULL;
+
+                       if (!first_mpc)
+                               first_mpc = mpc;
+                       else
+                               last_mpc->next = mpc;
+
+                       last_mpc = mpc;
+               }
+       }
+
+       /* choose a weighted random candidate */
+       decision = first;
+       selector = random(power);
+       last_power = 0;
+
+       /* select candidate, adjust GC data and cleanup local state */
+       decision = first;
+       last_mpc = NULL;
+       for (mpc = first_mpc; mpc; mpc = mpc->next) {
+               mpc->rt->u.dst.lastuse = jiffies;
+               if (last_power <= selector && selector < mpc->power)
+                       decision = mpc->rt;
+
+               last_power = mpc->power;
+               if (last_mpc)
+                       kfree(last_mpc);
+
+               last_mpc = mpc;
+       }
+
+       if (last_mpc) {
+               /* concurrent __multipath_flush may lead to !last_mpc */
+               kfree(last_mpc);
+       }
+
+       decision->u.dst.__use++;
+       *rp = decision;
+}
+
+static void wrandom_set_nhinfo(__u32 network,
+                              __u32 netmask,
+                              unsigned char prefixlen,
+                              const struct fib_nh *nh)
+{
+       const int state_idx = nh->nh_oif % MULTIPATH_STATE_SIZE;
+       struct multipath_route *r, *target_route = NULL;
+       struct multipath_dest *d, *target_dest = NULL;
+
+       /* store the weight information for a certain route */
+       spin_lock(&state[state_idx].lock);
+
+       /* find state entry for gateway or add one if necessary */
+       list_for_each_entry_rcu(r, &state[state_idx].head, list) {
+               if (r->gw == nh->nh_gw && r->oif == nh->nh_oif) {
+                       target_route = r;
+                       break;
+               }
+       }
+
+       if (!target_route) {
+               const size_t size_rt = sizeof(struct multipath_route);
+               target_route = (struct multipath_route *)
+                       kmalloc(size_rt, GFP_ATOMIC);
+
+               target_route->gw = nh->nh_gw;
+               target_route->oif = nh->nh_oif;
+               memset(&target_route->rcu, 0, sizeof(struct rcu_head));
+               INIT_LIST_HEAD(&target_route->dests);
+
+               list_add_rcu(&target_route->list, &state[state_idx].head);
+       }
+
+       /* find state entry for destination or add one if necessary */
+       list_for_each_entry_rcu(d, &target_route->dests, list) {
+               if (d->nh_info == nh) {
+                       target_dest = d;
+                       break;
+               }
+       }
+
+       if (!target_dest) {
+               const size_t size_dst = sizeof(struct multipath_dest);
+               target_dest = (struct multipath_dest*)
+                       kmalloc(size_dst, GFP_ATOMIC);
+
+               target_dest->nh_info = nh;
+               target_dest->network = network;
+               target_dest->netmask = netmask;
+               target_dest->prefixlen = prefixlen;
+               memset(&target_dest->rcu, 0, sizeof(struct rcu_head));
+
+               list_add_rcu(&target_dest->list, &target_route->dests);
+       }
+       /* else: we already stored this info for another destination =>
+        * we are finished
+        */
+
+       spin_unlock(&state[state_idx].lock);
+}
+
+static void __multipath_free(struct rcu_head *head)
+{
+       struct multipath_route *rt = container_of(head, struct multipath_route,
+                                                 rcu);
+       kfree(rt);
+}
+
+static void __multipath_free_dst(struct rcu_head *head)
+{
+       struct multipath_dest *dst = container_of(head,
+                                                 struct multipath_dest,
+                                                 rcu);
+       kfree(dst);
+}
+
+static void wrandom_flush(void)
+{
+       int i;
+
+       /* defere delete to all entries */
+       for (i = 0; i < MULTIPATH_STATE_SIZE; ++i) {
+               struct multipath_route *r;
+
+               spin_lock(&state[i].lock);
+               list_for_each_entry_rcu(r, &state[i].head, list) {
+                       struct multipath_dest *d;
+                       list_for_each_entry_rcu(d, &r->dests, list) {
+                               list_del_rcu(&d->list);
+                               call_rcu(&d->rcu,
+                                        __multipath_free_dst);
+                       }
+                       list_del_rcu(&r->list);
+                       call_rcu(&r->rcu,
+                                __multipath_free);
+               }
+
+               spin_unlock(&state[i].lock);
+       }
+}
+
+static struct ip_mp_alg_ops wrandom_ops = {
+       .mp_alg_select_route    =       wrandom_select_route,
+       .mp_alg_flush           =       wrandom_flush,
+       .mp_alg_set_nhinfo      =       wrandom_set_nhinfo,
+};
+
+static int __init wrandom_init(void)
+{
+       wrandom_init_state();
+
+       return multipath_alg_register(&wrandom_ops, IP_MP_ALG_WRANDOM);
+}
+
+static void __exit wrandom_exit(void)
+{
+       multipath_alg_unregister(&wrandom_ops, IP_MP_ALG_WRANDOM);
+}
+
+module_init(wrandom_init);
+module_exit(wrandom_exit);
+MODULE_LICENSE("GPL");
diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c
new file mode 100644 (file)
index 0000000..dfb300b
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ * net/sched/cls_basic.c       Basic Packet Classifier.
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the 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:    Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/rtnetlink.h>
+#include <linux/skbuff.h>
+#include <net/act_api.h>
+#include <net/pkt_cls.h>
+
+struct basic_head
+{
+       u32                     hgenerator;
+       struct list_head        flist;
+};
+
+struct basic_filter
+{
+       u32                     handle;
+       struct tcf_exts         exts;
+       struct tcf_ematch_tree  ematches;
+       struct tcf_result       res;
+       struct list_head        link;
+};
+
+static struct tcf_ext_map basic_ext_map = {
+       .action = TCA_BASIC_ACT,
+       .police = TCA_BASIC_POLICE
+};
+
+static int basic_classify(struct sk_buff *skb, struct tcf_proto *tp,
+                         struct tcf_result *res)
+{
+       int r;
+       struct basic_head *head = (struct basic_head *) tp->root;
+       struct basic_filter *f;
+
+       list_for_each_entry(f, &head->flist, link) {
+               if (!tcf_em_tree_match(skb, &f->ematches, NULL))
+                       continue;
+               *res = f->res;
+               r = tcf_exts_exec(skb, &f->exts, res);
+               if (r < 0)
+                       continue;
+               return r;
+       }
+       return -1;
+}
+
+static unsigned long basic_get(struct tcf_proto *tp, u32 handle)
+{
+       unsigned long l = 0UL;
+       struct basic_head *head = (struct basic_head *) tp->root;
+       struct basic_filter *f;
+
+       if (head == NULL)
+               return 0UL;
+
+       list_for_each_entry(f, &head->flist, link)
+               if (f->handle == handle)
+                       l = (unsigned long) f;
+
+       return l;
+}
+
+static void basic_put(struct tcf_proto *tp, unsigned long f)
+{
+}
+
+static int basic_init(struct tcf_proto *tp)
+{
+       return 0;
+}
+
+static inline void basic_delete_filter(struct tcf_proto *tp,
+                                      struct basic_filter *f)
+{
+       tcf_unbind_filter(tp, &f->res);
+       tcf_exts_destroy(tp, &f->exts);
+       tcf_em_tree_destroy(tp, &f->ematches);
+       kfree(f);
+}
+
+static void basic_destroy(struct tcf_proto *tp)
+{
+       struct basic_head *head = (struct basic_head *) xchg(&tp->root, NULL);
+       struct basic_filter *f, *n;
+       
+       list_for_each_entry_safe(f, n, &head->flist, link) {
+               list_del(&f->link);
+               basic_delete_filter(tp, f);
+       }
+}
+
+static int basic_delete(struct tcf_proto *tp, unsigned long arg)
+{
+       struct basic_head *head = (struct basic_head *) tp->root;
+       struct basic_filter *t, *f = (struct basic_filter *) arg;
+
+       list_for_each_entry(t, &head->flist, link)
+               if (t == f) {
+                       tcf_tree_lock(tp);
+                       list_del(&t->link);
+                       tcf_tree_unlock(tp);
+                       basic_delete_filter(tp, t);
+                       return 0;
+               }
+
+       return -ENOENT;
+}
+
+static inline int basic_set_parms(struct tcf_proto *tp, struct basic_filter *f,
+                                 unsigned long base, struct rtattr **tb,
+                                 struct rtattr *est)
+{
+       int err = -EINVAL;
+       struct tcf_exts e;
+       struct tcf_ematch_tree t;
+
+       if (tb[TCA_BASIC_CLASSID-1])
+               if (RTA_PAYLOAD(tb[TCA_BASIC_CLASSID-1]) < sizeof(u32))
+                       return err;
+
+       err = tcf_exts_validate(tp, tb, est, &e, &basic_ext_map);
+       if (err < 0)
+               return err;
+
+       err = tcf_em_tree_validate(tp, tb[TCA_BASIC_EMATCHES-1], &t);
+       if (err < 0)
+               goto errout;
+
+       if (tb[TCA_BASIC_CLASSID-1]) {
+               f->res.classid = *(u32*)RTA_DATA(tb[TCA_BASIC_CLASSID-1]);
+               tcf_bind_filter(tp, &f->res, base);
+       }
+
+       tcf_exts_change(tp, &f->exts, &e);
+       tcf_em_tree_change(tp, &f->ematches, &t);
+
+       return 0;
+errout:
+       tcf_exts_destroy(tp, &e);
+       return err;
+}
+
+static int basic_change(struct tcf_proto *tp, unsigned long base, u32 handle,
+                       struct rtattr **tca, unsigned long *arg)
+{
+       int err = -EINVAL;
+       struct basic_head *head = (struct basic_head *) tp->root;
+       struct rtattr *tb[TCA_BASIC_MAX];
+       struct basic_filter *f = (struct basic_filter *) *arg;
+
+       if (tca[TCA_OPTIONS-1] == NULL)
+               return -EINVAL;
+
+       if (rtattr_parse_nested(tb, TCA_BASIC_MAX, tca[TCA_OPTIONS-1]) < 0)
+               return -EINVAL;
+
+       if (f != NULL) {
+               if (handle && f->handle != handle)
+                       return -EINVAL;
+               return basic_set_parms(tp, f, base, tb, tca[TCA_RATE-1]);
+       }
+
+       err = -ENOBUFS;
+       if (head == NULL) {
+               head = kmalloc(sizeof(*head), GFP_KERNEL);
+               if (head == NULL)
+                       goto errout;
+
+               memset(head, 0, sizeof(*head));
+               INIT_LIST_HEAD(&head->flist);
+               tp->root = head;
+       }
+
+       f = kmalloc(sizeof(*f), GFP_KERNEL);
+       if (f == NULL)
+               goto errout;
+       memset(f, 0, sizeof(*f));
+
+       err = -EINVAL;
+       if (handle)
+               f->handle = handle;
+       else {
+               int i = 0x80000000;
+               do {
+                       if (++head->hgenerator == 0x7FFFFFFF)
+                               head->hgenerator = 1;
+               } while (--i > 0 && basic_get(tp, head->hgenerator));
+
+               if (i <= 0) {
+                       printk(KERN_ERR "Insufficient number of handles\n");
+                       goto errout;
+               }
+
+               f->handle = head->hgenerator;
+       }
+
+       err = basic_set_parms(tp, f, base, tb, tca[TCA_RATE-1]);
+       if (err < 0)
+               goto errout;
+
+       tcf_tree_lock(tp);
+       list_add(&f->link, &head->flist);
+       tcf_tree_unlock(tp);
+       *arg = (unsigned long) f;
+
+       return 0;
+errout:
+       if (*arg == 0UL && f)
+               kfree(f);
+
+       return err;
+}
+
+static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg)
+{
+       struct basic_head *head = (struct basic_head *) tp->root;
+       struct basic_filter *f;
+
+       list_for_each_entry(f, &head->flist, link) {
+               if (arg->count < arg->skip)
+                       goto skip;
+
+               if (arg->fn(tp, (unsigned long) f, arg) < 0) {
+                       arg->stop = 1;
+                       break;
+               }
+skip:
+               arg->count++;
+       }
+}
+
+static int basic_dump(struct tcf_proto *tp, unsigned long fh,
+                     struct sk_buff *skb, struct tcmsg *t)
+{
+       struct basic_filter *f = (struct basic_filter *) fh;
+       unsigned char *b = skb->tail;
+       struct rtattr *rta;
+
+       if (f == NULL)
+               return skb->len;
+
+       t->tcm_handle = f->handle;
+
+       rta = (struct rtattr *) b;
+       RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
+
+       if (f->res.classid)
+               RTA_PUT(skb, TCA_BASIC_CLASSID, sizeof(u32), &f->res.classid);
+
+       if (tcf_exts_dump(skb, &f->exts, &basic_ext_map) < 0 ||
+           tcf_em_tree_dump(skb, &f->ematches, TCA_BASIC_EMATCHES) < 0)
+               goto rtattr_failure;
+
+       rta->rta_len = (skb->tail - b);
+       return skb->len;
+
+rtattr_failure:
+       skb_trim(skb, b - skb->data);
+       return -1;
+}
+
+static struct tcf_proto_ops cls_basic_ops = {
+       .kind           =       "basic",
+       .classify       =       basic_classify,
+       .init           =       basic_init,
+       .destroy        =       basic_destroy,
+       .get            =       basic_get,
+       .put            =       basic_put,
+       .change         =       basic_change,
+       .delete         =       basic_delete,
+       .walk           =       basic_walk,
+       .dump           =       basic_dump,
+       .owner          =       THIS_MODULE,
+};
+
+static int __init init_basic(void)
+{
+       return register_tcf_proto_ops(&cls_basic_ops);
+}
+
+static void __exit exit_basic(void) 
+{
+       unregister_tcf_proto_ops(&cls_basic_ops);
+}
+
+module_init(init_basic)
+module_exit(exit_basic)
+MODULE_LICENSE("GPL");
+
diff --git a/net/sched/em_cmp.c b/net/sched/em_cmp.c
new file mode 100644 (file)
index 0000000..bf1f00f
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * net/sched/em_cmp.c  Simple packet data comparison ematch
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the 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:    Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/tc_ematch/tc_em_cmp.h>
+#include <net/pkt_cls.h>
+
+static inline int cmp_needs_transformation(struct tcf_em_cmp *cmp)
+{
+       return unlikely(cmp->flags & TCF_EM_CMP_TRANS);
+}
+
+static int em_cmp_match(struct sk_buff *skb, struct tcf_ematch *em,
+                       struct tcf_pkt_info *info)
+{
+       struct tcf_em_cmp *cmp = (struct tcf_em_cmp *) em->data;
+       unsigned char *ptr = tcf_get_base_ptr(skb, cmp->layer) + cmp->off;
+       u32 val = 0;
+
+       if (!tcf_valid_offset(skb, ptr, cmp->align))
+               return 0;
+
+       switch (cmp->align) {
+               case TCF_EM_ALIGN_U8:
+                       val = *ptr;
+                       break;
+
+               case TCF_EM_ALIGN_U16:
+                       val = *ptr << 8;
+                       val |= *(ptr+1);
+
+                       if (cmp_needs_transformation(cmp))
+                               val = be16_to_cpu(val);
+                       break;
+
+               case TCF_EM_ALIGN_U32:
+                       /* Worth checking boundries? The branching seems
+                        * to get worse. Visit again. */
+                       val = *ptr << 24;
+                       val |= *(ptr+1) << 16;
+                       val |= *(ptr+2) << 8;
+                       val |= *(ptr+3);
+
+                       if (cmp_needs_transformation(cmp))
+                               val = be32_to_cpu(val);
+                       break;
+
+               default:
+                       return 0;
+       }
+
+       if (cmp->mask)
+               val &= cmp->mask;
+
+       switch (cmp->opnd) {
+               case TCF_EM_OPND_EQ:
+                       return val == cmp->val;
+               case TCF_EM_OPND_LT:
+                       return val < cmp->val;
+               case TCF_EM_OPND_GT:
+                       return val > cmp->val;
+       }
+
+       return 0;
+}
+
+static struct tcf_ematch_ops em_cmp_ops = {
+       .kind     = TCF_EM_CMP,
+       .datalen  = sizeof(struct tcf_em_cmp),
+       .match    = em_cmp_match,
+       .owner    = THIS_MODULE,
+       .link     = LIST_HEAD_INIT(em_cmp_ops.link)
+};
+
+static int __init init_em_cmp(void)
+{
+       return tcf_em_register(&em_cmp_ops);
+}
+
+static void __exit exit_em_cmp(void) 
+{
+       tcf_em_unregister(&em_cmp_ops);
+}
+
+MODULE_LICENSE("GPL");
+
+module_init(init_em_cmp);
+module_exit(exit_em_cmp);
+
diff --git a/net/sched/em_meta.c b/net/sched/em_meta.c
new file mode 100644 (file)
index 0000000..48bb23c
--- /dev/null
@@ -0,0 +1,904 @@
+/*
+ * net/sched/em_meta.c Metadata ematch
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the 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:    Thomas Graf <tgraf@suug.ch>
+ *
+ * ==========================================================================
+ * 
+ *     The metadata ematch compares two meta objects where each object
+ *     represents either a meta value stored in the kernel or a static
+ *     value provided by userspace. The objects are not provided by
+ *     userspace itself but rather a definition providing the information
+ *     to build them. Every object is of a certain type which must be
+ *     equal to the object it is being compared to.
+ *
+ *     The definition of a objects conists of the type (meta type), a
+ *     identifier (meta id) and additional type specific information.
+ *     The meta id is either TCF_META_TYPE_VALUE for values provided by
+ *     userspace or a index to the meta operations table consisting of
+ *     function pointers to type specific meta data collectors returning
+ *     the value of the requested meta value.
+ *
+ *              lvalue                                   rvalue
+ *           +-----------+                           +-----------+
+ *           | type: INT |                           | type: INT |
+ *      def  | id: INDEV |                           | id: VALUE |
+ *           | data:     |                           | data: 3   |
+ *           +-----------+                           +-----------+
+ *                 |                                       |
+ *                 ---> meta_ops[INT][INDEV](...)          |
+ *                           |                             |
+ *                 -----------                             |
+ *                 V                                       V
+ *           +-----------+                           +-----------+
+ *           | type: INT |                           | type: INT |
+ *      obj  | id: INDEV |                           | id: VALUE |
+ *           | data: 2   |<--data got filled out     | data: 3   |
+ *           +-----------+                           +-----------+
+ *                 |                                         |
+ *                 --------------> 2  equals 3 <--------------
+ *
+ *     This is a simplified schema, the complexity varies depending
+ *     on the meta type. Obviously, the length of the data must also
+ *     be provided for non-numeric types.
+ *
+ *     Additionaly, type dependant modifiers such as shift operators
+ *     or mask may be applied to extend the functionaliy. As of now,
+ *     the variable length type supports shifting the byte string to
+ *     the right, eating up any number of octets and thus supporting
+ *     wildcard interface name comparisons such as "ppp%" matching
+ *     ppp0..9.
+ *
+ *     NOTE: Certain meta values depend on other subsystems and are
+ *           only available if that subsytem is enabled in the kernel.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/random.h>
+#include <linux/tc_ematch/tc_em_meta.h>
+#include <net/dst.h>
+#include <net/route.h>
+#include <net/pkt_cls.h>
+#include <net/sock.h>
+
+struct meta_obj
+{
+       unsigned long           value;
+       unsigned int            len;
+};
+
+struct meta_value
+{
+       struct tcf_meta_val     hdr;
+       unsigned long           val;
+       unsigned int            len;
+};
+
+struct meta_match
+{
+       struct meta_value       lvalue;
+       struct meta_value       rvalue;
+};
+
+static inline int meta_id(struct meta_value *v)
+{
+       return TCF_META_ID(v->hdr.kind);
+}
+
+static inline int meta_type(struct meta_value *v)
+{
+       return TCF_META_TYPE(v->hdr.kind);
+}
+
+#define META_COLLECTOR(FUNC) static void meta_##FUNC(struct sk_buff *skb, \
+       struct tcf_pkt_info *info, struct meta_value *v, \
+       struct meta_obj *dst, int *err)
+
+/**************************************************************************
+ * System status & misc
+ **************************************************************************/
+
+META_COLLECTOR(int_random)
+{
+       get_random_bytes(&dst->value, sizeof(dst->value));
+}
+
+static inline unsigned long fixed_loadavg(int load)
+{
+       int rnd_load = load + (FIXED_1/200);
+       int rnd_frac = ((rnd_load & (FIXED_1-1)) * 100) >> FSHIFT;
+
+       return ((rnd_load >> FSHIFT) * 100) + rnd_frac;
+}
+
+META_COLLECTOR(int_loadavg_0)
+{
+       dst->value = fixed_loadavg(avenrun[0]);
+}
+
+META_COLLECTOR(int_loadavg_1)
+{
+       dst->value = fixed_loadavg(avenrun[1]);
+}
+
+META_COLLECTOR(int_loadavg_2)
+{
+       dst->value = fixed_loadavg(avenrun[2]);
+}
+
+/**************************************************************************
+ * Device names & indices
+ **************************************************************************/
+
+static inline int int_dev(struct net_device *dev, struct meta_obj *dst)
+{
+       if (unlikely(dev == NULL))
+               return -1;
+
+       dst->value = dev->ifindex;
+       return 0;
+}
+
+static inline int var_dev(struct net_device *dev, struct meta_obj *dst)
+{
+       if (unlikely(dev == NULL))
+               return -1;
+
+       dst->value = (unsigned long) dev->name;
+       dst->len = strlen(dev->name);
+       return 0;
+}
+
+META_COLLECTOR(int_dev)
+{
+       *err = int_dev(skb->dev, dst);
+}
+
+META_COLLECTOR(var_dev)
+{
+       *err = var_dev(skb->dev, dst);
+}
+
+META_COLLECTOR(int_indev)
+{
+       *err = int_dev(skb->input_dev, dst);
+}
+
+META_COLLECTOR(var_indev)
+{
+       *err = var_dev(skb->input_dev, dst);
+}
+
+META_COLLECTOR(int_realdev)
+{
+       *err = int_dev(skb->real_dev, dst);
+}
+
+META_COLLECTOR(var_realdev)
+{
+       *err = var_dev(skb->real_dev, dst);
+}
+
+/**************************************************************************
+ * skb attributes
+ **************************************************************************/
+
+META_COLLECTOR(int_priority)
+{
+       dst->value = skb->priority;
+}
+
+META_COLLECTOR(int_protocol)
+{
+       /* Let userspace take care of the byte ordering */
+       dst->value = skb->protocol;
+}
+
+META_COLLECTOR(int_security)
+{
+       dst->value = skb->security;
+}
+
+META_COLLECTOR(int_pkttype)
+{
+       dst->value = skb->pkt_type;
+}
+
+META_COLLECTOR(int_pktlen)
+{
+       dst->value = skb->len;
+}
+
+META_COLLECTOR(int_datalen)
+{
+       dst->value = skb->data_len;
+}
+
+META_COLLECTOR(int_maclen)
+{
+       dst->value = skb->mac_len;
+}
+
+/**************************************************************************
+ * Netfilter
+ **************************************************************************/
+
+#ifdef CONFIG_NETFILTER
+META_COLLECTOR(int_nfmark)
+{
+       dst->value = skb->nfmark;
+}
+#endif
+
+/**************************************************************************
+ * Traffic Control
+ **************************************************************************/
+
+META_COLLECTOR(int_tcindex)
+{
+       dst->value = skb->tc_index;
+}
+
+#ifdef CONFIG_NET_CLS_ACT
+META_COLLECTOR(int_tcverd)
+{
+       dst->value = skb->tc_verd;
+}
+
+META_COLLECTOR(int_tcclassid)
+{
+       dst->value = skb->tc_classid;
+}
+#endif
+
+/**************************************************************************
+ * Routing
+ **************************************************************************/
+
+#ifdef CONFIG_NET_CLS_ROUTE
+META_COLLECTOR(int_rtclassid)
+{
+       if (unlikely(skb->dst == NULL))
+               *err = -1;
+       else
+               dst->value = skb->dst->tclassid;
+}
+#endif
+
+META_COLLECTOR(int_rtiif)
+{
+       if (unlikely(skb->dst == NULL))
+               *err = -1;
+       else
+               dst->value = ((struct rtable*) skb->dst)->fl.iif;
+}
+
+/**************************************************************************
+ * Socket Attributes
+ **************************************************************************/
+
+#define SKIP_NONLOCAL(skb)                     \
+       if (unlikely(skb->sk == NULL)) {        \
+               *err = -1;                      \
+               return;                         \
+       }
+
+META_COLLECTOR(int_sk_family)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_family;
+}
+
+META_COLLECTOR(int_sk_state)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_state;
+}
+
+META_COLLECTOR(int_sk_reuse)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_reuse;
+}
+
+META_COLLECTOR(int_sk_bound_if)
+{
+       SKIP_NONLOCAL(skb);
+       /* No error if bound_dev_if is 0, legal userspace check */
+       dst->value = skb->sk->sk_bound_dev_if;
+}
+
+META_COLLECTOR(var_sk_bound_if)
+{
+       SKIP_NONLOCAL(skb);
+
+        if (skb->sk->sk_bound_dev_if == 0) {
+               dst->value = (unsigned long) "any";
+               dst->len = 3;
+        } else  {
+               struct net_device *dev;
+               
+               dev = dev_get_by_index(skb->sk->sk_bound_dev_if);
+               *err = var_dev(dev, dst);
+               if (dev)
+                       dev_put(dev);
+        }
+}
+
+META_COLLECTOR(int_sk_refcnt)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = atomic_read(&skb->sk->sk_refcnt);
+}
+
+META_COLLECTOR(int_sk_rcvbuf)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_rcvbuf;
+}
+
+META_COLLECTOR(int_sk_shutdown)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_shutdown;
+}
+
+META_COLLECTOR(int_sk_proto)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_protocol;
+}
+
+META_COLLECTOR(int_sk_type)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_type;
+}
+
+META_COLLECTOR(int_sk_rmem_alloc)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = atomic_read(&skb->sk->sk_rmem_alloc);
+}
+
+META_COLLECTOR(int_sk_wmem_alloc)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = atomic_read(&skb->sk->sk_wmem_alloc);
+}
+
+META_COLLECTOR(int_sk_omem_alloc)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = atomic_read(&skb->sk->sk_omem_alloc);
+}
+
+META_COLLECTOR(int_sk_rcv_qlen)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_receive_queue.qlen;
+}
+
+META_COLLECTOR(int_sk_snd_qlen)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_write_queue.qlen;
+}
+
+META_COLLECTOR(int_sk_wmem_queued)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_wmem_queued;
+}
+
+META_COLLECTOR(int_sk_fwd_alloc)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_forward_alloc;
+}
+
+META_COLLECTOR(int_sk_sndbuf)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_sndbuf;
+}
+
+META_COLLECTOR(int_sk_alloc)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_allocation;
+}
+
+META_COLLECTOR(int_sk_route_caps)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_route_caps;
+}
+
+META_COLLECTOR(int_sk_hashent)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_hashent;
+}
+
+META_COLLECTOR(int_sk_lingertime)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_lingertime / HZ;
+}
+
+META_COLLECTOR(int_sk_err_qlen)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_error_queue.qlen;
+}
+
+META_COLLECTOR(int_sk_ack_bl)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_ack_backlog;
+}
+
+META_COLLECTOR(int_sk_max_ack_bl)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_max_ack_backlog;
+}
+
+META_COLLECTOR(int_sk_prio)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_priority;
+}
+
+META_COLLECTOR(int_sk_rcvlowat)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_rcvlowat;
+}
+
+META_COLLECTOR(int_sk_rcvtimeo)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_rcvtimeo / HZ;
+}
+
+META_COLLECTOR(int_sk_sndtimeo)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_sndtimeo / HZ;
+}
+
+META_COLLECTOR(int_sk_sendmsg_off)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_sndmsg_off;
+}
+
+META_COLLECTOR(int_sk_write_pend)
+{
+       SKIP_NONLOCAL(skb);
+       dst->value = skb->sk->sk_write_pending;
+}
+
+/**************************************************************************
+ * Meta value collectors assignment table
+ **************************************************************************/
+
+struct meta_ops
+{
+       void            (*get)(struct sk_buff *, struct tcf_pkt_info *,
+                              struct meta_value *, struct meta_obj *, int *);
+};
+
+#define META_ID(name) TCF_META_ID_##name
+#define META_FUNC(name) { .get = meta_##name }
+
+/* Meta value operations table listing all meta value collectors and
+ * assigns them to a type and meta id. */
+static struct meta_ops __meta_ops[TCF_META_TYPE_MAX+1][TCF_META_ID_MAX+1] = {
+       [TCF_META_TYPE_VAR] = {
+               [META_ID(DEV)]                  = META_FUNC(var_dev),
+               [META_ID(INDEV)]                = META_FUNC(var_indev),
+               [META_ID(REALDEV)]              = META_FUNC(var_realdev),
+               [META_ID(SK_BOUND_IF)]          = META_FUNC(var_sk_bound_if),
+       },
+       [TCF_META_TYPE_INT] = {
+               [META_ID(RANDOM)]               = META_FUNC(int_random),
+               [META_ID(LOADAVG_0)]            = META_FUNC(int_loadavg_0),
+               [META_ID(LOADAVG_1)]            = META_FUNC(int_loadavg_1),
+               [META_ID(LOADAVG_2)]            = META_FUNC(int_loadavg_2),
+               [META_ID(DEV)]                  = META_FUNC(int_dev),
+               [META_ID(INDEV)]                = META_FUNC(int_indev),
+               [META_ID(REALDEV)]              = META_FUNC(int_realdev),
+               [META_ID(PRIORITY)]             = META_FUNC(int_priority),
+               [META_ID(PROTOCOL)]             = META_FUNC(int_protocol),
+               [META_ID(SECURITY)]             = META_FUNC(int_security),
+               [META_ID(PKTTYPE)]              = META_FUNC(int_pkttype),
+               [META_ID(PKTLEN)]               = META_FUNC(int_pktlen),
+               [META_ID(DATALEN)]              = META_FUNC(int_datalen),
+               [META_ID(MACLEN)]               = META_FUNC(int_maclen),
+#ifdef CONFIG_NETFILTER
+               [META_ID(NFMARK)]               = META_FUNC(int_nfmark),
+#endif
+               [META_ID(TCINDEX)]              = META_FUNC(int_tcindex),
+#ifdef CONFIG_NET_CLS_ACT
+               [META_ID(TCVERDICT)]            = META_FUNC(int_tcverd),
+               [META_ID(TCCLASSID)]            = META_FUNC(int_tcclassid),
+#endif
+#ifdef CONFIG_NET_CLS_ROUTE
+               [META_ID(RTCLASSID)]            = META_FUNC(int_rtclassid),
+#endif
+               [META_ID(RTIIF)]                = META_FUNC(int_rtiif),
+               [META_ID(SK_FAMILY)]            = META_FUNC(int_sk_family),
+               [META_ID(SK_STATE)]             = META_FUNC(int_sk_state),
+               [META_ID(SK_REUSE)]             = META_FUNC(int_sk_reuse),
+               [META_ID(SK_BOUND_IF)]          = META_FUNC(int_sk_bound_if),
+               [META_ID(SK_REFCNT)]            = META_FUNC(int_sk_refcnt),
+               [META_ID(SK_RCVBUF)]            = META_FUNC(int_sk_rcvbuf),
+               [META_ID(SK_SNDBUF)]            = META_FUNC(int_sk_sndbuf),
+               [META_ID(SK_SHUTDOWN)]          = META_FUNC(int_sk_shutdown),
+               [META_ID(SK_PROTO)]             = META_FUNC(int_sk_proto),
+               [META_ID(SK_TYPE)]              = META_FUNC(int_sk_type),
+               [META_ID(SK_RMEM_ALLOC)]        = META_FUNC(int_sk_rmem_alloc),
+               [META_ID(SK_WMEM_ALLOC)]        = META_FUNC(int_sk_wmem_alloc),
+               [META_ID(SK_OMEM_ALLOC)]        = META_FUNC(int_sk_omem_alloc),
+               [META_ID(SK_WMEM_QUEUED)]       = META_FUNC(int_sk_wmem_queued),
+               [META_ID(SK_RCV_QLEN)]          = META_FUNC(int_sk_rcv_qlen),
+               [META_ID(SK_SND_QLEN)]          = META_FUNC(int_sk_snd_qlen),
+               [META_ID(SK_ERR_QLEN)]          = META_FUNC(int_sk_err_qlen),
+               [META_ID(SK_FORWARD_ALLOCS)]    = META_FUNC(int_sk_fwd_alloc),
+               [META_ID(SK_ALLOCS)]            = META_FUNC(int_sk_alloc),
+               [META_ID(SK_ROUTE_CAPS)]        = META_FUNC(int_sk_route_caps),
+               [META_ID(SK_HASHENT)]           = META_FUNC(int_sk_hashent),
+               [META_ID(SK_LINGERTIME)]        = META_FUNC(int_sk_lingertime),
+               [META_ID(SK_ACK_BACKLOG)]       = META_FUNC(int_sk_ack_bl),
+               [META_ID(SK_MAX_ACK_BACKLOG)]   = META_FUNC(int_sk_max_ack_bl),
+               [META_ID(SK_PRIO)]              = META_FUNC(int_sk_prio),
+               [META_ID(SK_RCVLOWAT)]          = META_FUNC(int_sk_rcvlowat),
+               [META_ID(SK_RCVTIMEO)]          = META_FUNC(int_sk_rcvtimeo),
+               [META_ID(SK_SNDTIMEO)]          = META_FUNC(int_sk_sndtimeo),
+               [META_ID(SK_SENDMSG_OFF)]       = META_FUNC(int_sk_sendmsg_off),
+               [META_ID(SK_WRITE_PENDING)]     = META_FUNC(int_sk_write_pend),
+       }
+};
+
+static inline struct meta_ops * meta_ops(struct meta_value *val)
+{
+       return &__meta_ops[meta_type(val)][meta_id(val)];
+}
+
+/**************************************************************************
+ * Type specific operations for TCF_META_TYPE_VAR
+ **************************************************************************/
+
+static int meta_var_compare(struct meta_obj *a, struct meta_obj *b)
+{
+       int r = a->len - b->len;
+
+       if (r == 0)
+               r = memcmp((void *) a->value, (void *) b->value, a->len);
+
+       return r;
+}
+
+static int meta_var_change(struct meta_value *dst, struct rtattr *rta)
+{
+       int len = RTA_PAYLOAD(rta);
+
+       dst->val = (unsigned long) kmalloc(len, GFP_KERNEL);
+       if (dst->val == 0UL)
+               return -ENOMEM;
+       memcpy((void *) dst->val, RTA_DATA(rta), len);
+       dst->len = len;
+       return 0;
+}
+
+static void meta_var_destroy(struct meta_value *v)
+{
+       if (v->val)
+               kfree((void *) v->val);
+}
+
+static void meta_var_apply_extras(struct meta_value *v,
+                                 struct meta_obj *dst)
+{
+       int shift = v->hdr.shift;
+
+       if (shift && shift < dst->len)
+               dst->len -= shift;
+}
+
+static int meta_var_dump(struct sk_buff *skb, struct meta_value *v, int tlv)
+{
+       if (v->val && v->len)
+               RTA_PUT(skb, tlv, v->len, (void *) v->val);
+       return 0;
+
+rtattr_failure:
+       return -1;
+}
+
+/**************************************************************************
+ * Type specific operations for TCF_META_TYPE_INT
+ **************************************************************************/
+
+static int meta_int_compare(struct meta_obj *a, struct meta_obj *b)
+{
+       /* Let gcc optimize it, the unlikely is not really based on
+        * some numbers but jump free code for mismatches seems
+        * more logical. */
+       if (unlikely(a->value == b->value))
+               return 0;
+       else if (a->value < b->value)
+               return -1;
+       else
+               return 1;
+}
+
+static int meta_int_change(struct meta_value *dst, struct rtattr *rta)
+{
+       if (RTA_PAYLOAD(rta) >= sizeof(unsigned long)) {
+               dst->val = *(unsigned long *) RTA_DATA(rta);
+               dst->len = sizeof(unsigned long);
+       } else if (RTA_PAYLOAD(rta) == sizeof(u32)) {
+               dst->val = *(u32 *) RTA_DATA(rta);
+               dst->len = sizeof(u32);
+       } else
+               return -EINVAL;
+
+       return 0;
+}
+
+static void meta_int_apply_extras(struct meta_value *v,
+                                 struct meta_obj *dst)
+{
+       if (v->hdr.shift)
+               dst->value >>= v->hdr.shift;
+
+       if (v->val)
+               dst->value &= v->val;
+}
+
+static int meta_int_dump(struct sk_buff *skb, struct meta_value *v, int tlv)
+{
+       if (v->len == sizeof(unsigned long))
+               RTA_PUT(skb, tlv, sizeof(unsigned long), &v->val);
+       else if (v->len == sizeof(u32)) {
+               u32 d = v->val;
+               RTA_PUT(skb, tlv, sizeof(d), &d);
+       }
+
+       return 0;
+
+rtattr_failure:
+       return -1;
+}
+
+/**************************************************************************
+ * Type specific operations table
+ **************************************************************************/
+
+struct meta_type_ops
+{
+       void    (*destroy)(struct meta_value *);
+       int     (*compare)(struct meta_obj *, struct meta_obj *);
+       int     (*change)(struct meta_value *, struct rtattr *);
+       void    (*apply_extras)(struct meta_value *, struct meta_obj *);
+       int     (*dump)(struct sk_buff *, struct meta_value *, int);
+};
+
+static struct meta_type_ops __meta_type_ops[TCF_META_TYPE_MAX+1] = {
+       [TCF_META_TYPE_VAR] = {
+               .destroy = meta_var_destroy,
+               .compare = meta_var_compare,
+               .change = meta_var_change,
+               .apply_extras = meta_var_apply_extras,
+               .dump = meta_var_dump
+       },
+       [TCF_META_TYPE_INT] = {
+               .compare = meta_int_compare,
+               .change = meta_int_change,
+               .apply_extras = meta_int_apply_extras,
+               .dump = meta_int_dump
+       }
+};
+
+static inline struct meta_type_ops * meta_type_ops(struct meta_value *v)
+{
+       return &__meta_type_ops[meta_type(v)];
+}
+
+/**************************************************************************
+ * Core
+ **************************************************************************/
+
+static inline int meta_get(struct sk_buff *skb, struct tcf_pkt_info *info, 
+                          struct meta_value *v, struct meta_obj *dst)
+{
+       int err = 0;
+
+       if (meta_id(v) == TCF_META_ID_VALUE) {
+               dst->value = v->val;
+               dst->len = v->len;
+               return 0;
+       }
+
+       meta_ops(v)->get(skb, info, v, dst, &err);
+       if (err < 0)
+               return err;
+
+       if (meta_type_ops(v)->apply_extras)
+           meta_type_ops(v)->apply_extras(v, dst);
+
+       return 0;
+}
+
+static int em_meta_match(struct sk_buff *skb, struct tcf_ematch *m,
+                        struct tcf_pkt_info *info)
+{
+       int r;
+       struct meta_match *meta = (struct meta_match *) m->data;
+       struct meta_obj l_value, r_value;
+
+       if (meta_get(skb, info, &meta->lvalue, &l_value) < 0 ||
+           meta_get(skb, info, &meta->rvalue, &r_value) < 0)
+               return 0;
+
+       r = meta_type_ops(&meta->lvalue)->compare(&l_value, &r_value);
+
+       switch (meta->lvalue.hdr.op) {
+               case TCF_EM_OPND_EQ:
+                       return !r;
+               case TCF_EM_OPND_LT:
+                       return r < 0;
+               case TCF_EM_OPND_GT:
+                       return r > 0;
+       }
+
+       return 0;
+}
+
+static inline void meta_delete(struct meta_match *meta)
+{
+       struct meta_type_ops *ops = meta_type_ops(&meta->lvalue);
+
+       if (ops && ops->destroy) {
+               ops->destroy(&meta->lvalue);
+               ops->destroy(&meta->rvalue);
+       }
+
+       kfree(meta);
+}
+
+static inline int meta_change_data(struct meta_value *dst, struct rtattr *rta)
+{
+       if (rta) {
+               if (RTA_PAYLOAD(rta) == 0)
+                       return -EINVAL;
+
+               return meta_type_ops(dst)->change(dst, rta);
+       }
+
+       return 0;
+}
+
+static inline int meta_is_supported(struct meta_value *val)
+{
+       return (!meta_id(val) || meta_ops(val)->get);
+}
+
+static int em_meta_change(struct tcf_proto *tp, void *data, int len,
+                         struct tcf_ematch *m)
+{
+       int err = -EINVAL;
+       struct rtattr *tb[TCA_EM_META_MAX];
+       struct tcf_meta_hdr *hdr;
+       struct meta_match *meta = NULL;
+       
+       if (rtattr_parse(tb, TCA_EM_META_MAX, data, len) < 0)
+               goto errout;
+
+       if (tb[TCA_EM_META_HDR-1] == NULL ||
+           RTA_PAYLOAD(tb[TCA_EM_META_HDR-1]) < sizeof(*hdr))
+               goto errout;
+       hdr = RTA_DATA(tb[TCA_EM_META_HDR-1]);
+
+       if (TCF_META_TYPE(hdr->left.kind) != TCF_META_TYPE(hdr->right.kind) ||
+           TCF_META_TYPE(hdr->left.kind) > TCF_META_TYPE_MAX ||
+           TCF_META_ID(hdr->left.kind) > TCF_META_ID_MAX ||
+           TCF_META_ID(hdr->right.kind) > TCF_META_ID_MAX)
+               goto errout;
+
+       meta = kmalloc(sizeof(*meta), GFP_KERNEL);
+       if (meta == NULL)
+               goto errout;
+       memset(meta, 0, sizeof(*meta));
+
+       memcpy(&meta->lvalue.hdr, &hdr->left, sizeof(hdr->left));
+       memcpy(&meta->rvalue.hdr, &hdr->right, sizeof(hdr->right));
+
+       if (!meta_is_supported(&meta->lvalue) ||
+           !meta_is_supported(&meta->rvalue)) {
+               err = -EOPNOTSUPP;
+               goto errout;
+       }
+
+       if (meta_change_data(&meta->lvalue, tb[TCA_EM_META_LVALUE-1]) < 0 ||
+           meta_change_data(&meta->rvalue, tb[TCA_EM_META_RVALUE-1]) < 0)
+               goto errout;
+
+       m->datalen = sizeof(*meta);
+       m->data = (unsigned long) meta;
+
+       err = 0;
+errout:
+       if (err && meta)
+               meta_delete(meta);
+       return err;
+}
+
+static void em_meta_destroy(struct tcf_proto *tp, struct tcf_ematch *m)
+{
+       if (m)
+               meta_delete((struct meta_match *) m->data);
+}
+
+static int em_meta_dump(struct sk_buff *skb, struct tcf_ematch *em)
+{
+       struct meta_match *meta = (struct meta_match *) em->data;
+       struct tcf_meta_hdr hdr;
+       struct meta_type_ops *ops;
+
+       memset(&hdr, 0, sizeof(hdr));
+       memcpy(&hdr.left, &meta->lvalue.hdr, sizeof(hdr.left));
+       memcpy(&hdr.right, &meta->rvalue.hdr, sizeof(hdr.right));
+
+       RTA_PUT(skb, TCA_EM_META_HDR, sizeof(hdr), &hdr);
+
+       ops = meta_type_ops(&meta->lvalue);
+       if (ops->dump(skb, &meta->lvalue, TCA_EM_META_LVALUE) < 0 ||
+           ops->dump(skb, &meta->rvalue, TCA_EM_META_RVALUE) < 0)
+               goto rtattr_failure;
+
+       return 0;
+
+rtattr_failure:
+       return -1;
+}              
+
+static struct tcf_ematch_ops em_meta_ops = {
+       .kind     = TCF_EM_META,
+       .change   = em_meta_change,
+       .match    = em_meta_match,
+       .destroy  = em_meta_destroy,
+       .dump     = em_meta_dump,
+       .owner    = THIS_MODULE,
+       .link     = LIST_HEAD_INIT(em_meta_ops.link)
+};
+
+static int __init init_em_meta(void)
+{
+       return tcf_em_register(&em_meta_ops);
+}
+
+static void __exit exit_em_meta(void) 
+{
+       tcf_em_unregister(&em_meta_ops);
+}
+
+MODULE_LICENSE("GPL");
+
+module_init(init_em_meta);
+module_exit(exit_em_meta);
diff --git a/net/sched/em_nbyte.c b/net/sched/em_nbyte.c
new file mode 100644 (file)
index 0000000..71ea926
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * net/sched/em_nbyte.c        N-Byte ematch
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the 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:    Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/tc_ematch/tc_em_nbyte.h>
+#include <net/pkt_cls.h>
+
+struct nbyte_data
+{
+       struct tcf_em_nbyte     hdr;
+       char                    pattern[0];
+};
+       
+static int em_nbyte_change(struct tcf_proto *tp, void *data, int data_len,
+                          struct tcf_ematch *em)
+{
+       struct tcf_em_nbyte *nbyte = data;
+
+       if (data_len < sizeof(*nbyte) ||
+           data_len < (sizeof(*nbyte) + nbyte->len))
+               return -EINVAL;
+
+       em->datalen = sizeof(*nbyte) + nbyte->len;
+       em->data = (unsigned long) kmalloc(em->datalen, GFP_KERNEL);
+       if (em->data == 0UL)
+               return -ENOBUFS;
+
+       memcpy((void *) em->data, data, em->datalen);
+
+       return 0;
+}
+
+static int em_nbyte_match(struct sk_buff *skb, struct tcf_ematch *em,
+                         struct tcf_pkt_info *info)
+{
+       struct nbyte_data *nbyte = (struct nbyte_data *) em->data;
+       unsigned char *ptr = tcf_get_base_ptr(skb, nbyte->hdr.layer);
+
+       ptr += nbyte->hdr.off;
+
+       if (!tcf_valid_offset(skb, ptr, nbyte->hdr.len))
+               return 0;
+
+       return !memcmp(ptr + nbyte->hdr.off, nbyte->pattern, nbyte->hdr.len);
+}
+
+static struct tcf_ematch_ops em_nbyte_ops = {
+       .kind     = TCF_EM_NBYTE,
+       .change   = em_nbyte_change,
+       .match    = em_nbyte_match,
+       .owner    = THIS_MODULE,
+       .link     = LIST_HEAD_INIT(em_nbyte_ops.link)
+};
+
+static int __init init_em_nbyte(void)
+{
+       return tcf_em_register(&em_nbyte_ops);
+}
+
+static void __exit exit_em_nbyte(void) 
+{
+       tcf_em_unregister(&em_nbyte_ops);
+}
+
+MODULE_LICENSE("GPL");
+
+module_init(init_em_nbyte);
+module_exit(exit_em_nbyte);
diff --git a/net/sched/em_u32.c b/net/sched/em_u32.c
new file mode 100644 (file)
index 0000000..34e7e51
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * net/sched/em_u32.c  U32 Ematch
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the 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:    Thomas Graf <tgraf@suug.ch>
+ *             Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Based on net/sched/cls_u32.c
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <net/pkt_cls.h>
+
+static int em_u32_match(struct sk_buff *skb, struct tcf_ematch *em,
+                       struct tcf_pkt_info *info)
+{
+       struct tc_u32_key *key = (struct tc_u32_key *) em->data;
+       unsigned char *ptr = skb->nh.raw;
+       
+       if (info) {
+               if (info->ptr)
+                       ptr = info->ptr;
+               ptr += (info->nexthdr & key->offmask);
+       }
+
+       ptr += key->off;
+
+       if (!tcf_valid_offset(skb, ptr, sizeof(u32)))
+               return 0;
+       
+       return !(((*(u32*) ptr)  ^ key->val) & key->mask);
+}
+
+static struct tcf_ematch_ops em_u32_ops = {
+       .kind     = TCF_EM_U32,
+       .datalen  = sizeof(struct tc_u32_key),
+       .match    = em_u32_match,
+       .owner    = THIS_MODULE,
+       .link     = LIST_HEAD_INIT(em_u32_ops.link)
+};
+
+static int __init init_em_u32(void)
+{
+       return tcf_em_register(&em_u32_ops);
+}
+
+static void __exit exit_em_u32(void) 
+{
+       tcf_em_unregister(&em_u32_ops);
+}
+
+MODULE_LICENSE("GPL");
+
+module_init(init_em_u32);
+module_exit(exit_em_u32);
diff --git a/net/sched/ematch.c b/net/sched/ematch.c
new file mode 100644 (file)
index 0000000..ebfe2e7
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ * net/sched/ematch.c          Extended Match API
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the 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:    Thomas Graf <tgraf@suug.ch>
+ *
+ * ==========================================================================
+ *
+ * An extended match (ematch) is a small classification tool not worth
+ * writing a full classifier for. Ematches can be interconnected to form
+ * a logic expression and get attached to classifiers to extend their
+ * functionatlity.
+ *
+ * The userspace part transforms the logic expressions into an array
+ * consisting of multiple sequences of interconnected ematches separated
+ * by markers. Precedence is implemented by a special ematch kind
+ * referencing a sequence beyond the marker of the current sequence
+ * causing the current position in the sequence to be pushed onto a stack
+ * to allow the current position to be overwritten by the position referenced
+ * in the special ematch. Matching continues in the new sequence until a
+ * marker is reached causing the position to be restored from the stack.
+ *
+ * Example:
+ *          A AND (B1 OR B2) AND C AND D
+ *
+ *              ------->-PUSH-------
+ *    -->--    /         -->--      \   -->--
+ *   /     \  /         /     \      \ /     \
+ * +-------+-------+-------+-------+-------+--------+
+ * | A AND | B AND | C AND | D END | B1 OR | B2 END |
+ * +-------+-------+-------+-------+-------+--------+
+ *                    \                      /
+ *                     --------<-POP---------
+ *
+ * where B is a virtual ematch referencing to sequence starting with B1.
+ * 
+ * ==========================================================================
+ *
+ * How to write an ematch in 60 seconds
+ * ------------------------------------
+ * 
+ *   1) Provide a matcher function:
+ *      static int my_match(struct sk_buff *skb, struct tcf_ematch *m,
+ *                          struct tcf_pkt_info *info)
+ *      {
+ *             struct mydata *d = (struct mydata *) m->data;
+ *
+ *             if (...matching goes here...)
+ *                     return 1;
+ *             else
+ *                     return 0;
+ *      }
+ *
+ *   2) Fill out a struct tcf_ematch_ops:
+ *      static struct tcf_ematch_ops my_ops = {
+ *             .kind = unique id,
+ *             .datalen = sizeof(struct mydata),
+ *             .match = my_match,
+ *             .owner = THIS_MODULE,
+ *      };
+ *
+ *   3) Register/Unregister your ematch:
+ *      static int __init init_my_ematch(void)
+ *      {
+ *             return tcf_em_register(&my_ops);
+ *      }
+ *
+ *      static void __exit exit_my_ematch(void)
+ *      {
+ *             return tcf_em_unregister(&my_ops);
+ *      }
+ *
+ *      module_init(init_my_ematch);
+ *      module_exit(exit_my_ematch);
+ *
+ *   4) By now you should have two more seconds left, barely enough to
+ *      open up a beer to watch the compilation going.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/rtnetlink.h>
+#include <linux/skbuff.h>
+#include <net/pkt_cls.h>
+#include <config/net/ematch/stack.h>
+
+static LIST_HEAD(ematch_ops);
+static DEFINE_RWLOCK(ematch_mod_lock);
+
+static inline struct tcf_ematch_ops * tcf_em_lookup(u16 kind)
+{
+       struct tcf_ematch_ops *e = NULL;
+
+       read_lock(&ematch_mod_lock);
+       list_for_each_entry(e, &ematch_ops, link) {
+               if (kind == e->kind) {
+                       if (!try_module_get(e->owner))
+                               e = NULL;
+                       read_unlock(&ematch_mod_lock);
+                       return e;
+               }
+       }
+       read_unlock(&ematch_mod_lock);
+
+       return NULL;
+}
+
+/**
+ * tcf_em_register - register an extended match
+ * 
+ * @ops: ematch operations lookup table
+ *
+ * This function must be called by ematches to announce their presence.
+ * The given @ops must have kind set to a unique identifier and the
+ * callback match() must be implemented. All other callbacks are optional
+ * and a fallback implementation is used instead.
+ *
+ * Returns -EEXISTS if an ematch of the same kind has already registered.
+ */
+int tcf_em_register(struct tcf_ematch_ops *ops)
+{
+       int err = -EEXIST;
+       struct tcf_ematch_ops *e;
+
+       if (ops->match == NULL)
+               return -EINVAL;
+
+       write_lock(&ematch_mod_lock);
+       list_for_each_entry(e, &ematch_ops, link)
+               if (ops->kind == e->kind)
+                       goto errout;
+
+       list_add_tail(&ops->link, &ematch_ops);
+       err = 0;
+errout:
+       write_unlock(&ematch_mod_lock);
+       return err;
+}
+
+/**
+ * tcf_em_unregister - unregster and extended match
+ *
+ * @ops: ematch operations lookup table
+ *
+ * This function must be called by ematches to announce their disappearance
+ * for examples when the module gets unloaded. The @ops parameter must be
+ * the same as the one used for registration.
+ *
+ * Returns -ENOENT if no matching ematch was found.
+ */
+int tcf_em_unregister(struct tcf_ematch_ops *ops)
+{
+       int err = 0;
+       struct tcf_ematch_ops *e;
+
+       write_lock(&ematch_mod_lock);
+       list_for_each_entry(e, &ematch_ops, link) {
+               if (e == ops) {
+                       list_del(&e->link);
+                       goto out;
+               }
+       }
+
+       err = -ENOENT;
+out:
+       write_unlock(&ematch_mod_lock);
+       return err;
+}
+
+static inline struct tcf_ematch * tcf_em_get_match(struct tcf_ematch_tree *tree,
+                                                  int index)
+{
+       return &tree->matches[index];
+}
+
+
+static int tcf_em_validate(struct tcf_proto *tp,
+                          struct tcf_ematch_tree_hdr *tree_hdr,
+                          struct tcf_ematch *em, struct rtattr *rta, int idx)
+{
+       int err = -EINVAL;
+       struct tcf_ematch_hdr *em_hdr = RTA_DATA(rta);
+       int data_len = RTA_PAYLOAD(rta) - sizeof(*em_hdr);
+       void *data = (void *) em_hdr + sizeof(*em_hdr);
+
+       if (!TCF_EM_REL_VALID(em_hdr->flags))
+               goto errout;
+
+       if (em_hdr->kind == TCF_EM_CONTAINER) {
+               /* Special ematch called "container", carries an index
+                * referencing an external ematch sequence. */
+               u32 ref;
+
+               if (data_len < sizeof(ref))
+                       goto errout;
+               ref = *(u32 *) data;
+
+               if (ref >= tree_hdr->nmatches)
+                       goto errout;
+
+               /* We do not allow backward jumps to avoid loops and jumps
+                * to our own position are of course illegal. */
+               if (ref <= idx)
+                       goto errout;
+
+               
+               em->data = ref;
+       } else {
+               /* Note: This lookup will increase the module refcnt
+                * of the ematch module referenced. In case of a failure,
+                * a destroy function is called by the underlying layer
+                * which automatically releases the reference again, therefore
+                * the module MUST not be given back under any circumstances
+                * here. Be aware, the destroy function assumes that the
+                * module is held if the ops field is non zero. */
+               em->ops = tcf_em_lookup(em_hdr->kind);
+
+               if (em->ops == NULL) {
+                       err = -ENOENT;
+                       goto errout;
+               }
+
+               /* ematch module provides expected length of data, so we
+                * can do a basic sanity check. */
+               if (em->ops->datalen && data_len < em->ops->datalen)
+                       goto errout;
+
+               if (em->ops->change) {
+                       err = em->ops->change(tp, data, data_len, em);
+                       if (err < 0)
+                               goto errout;
+               } else if (data_len > 0) {
+                       /* ematch module doesn't provide an own change
+                        * procedure and expects us to allocate and copy
+                        * the ematch data.
+                        *
+                        * TCF_EM_SIMPLE may be specified stating that the
+                        * data only consists of a u32 integer and the module
+                        * does not expected a memory reference but rather
+                        * the value carried. */
+                       if (em_hdr->flags & TCF_EM_SIMPLE) {
+                               if (data_len < sizeof(u32))
+                                       goto errout;
+                               em->data = *(u32 *) data;
+                       } else {
+                               void *v = kmalloc(data_len, GFP_KERNEL);
+                               if (v == NULL) {
+                                       err = -ENOBUFS;
+                                       goto errout;
+                               }
+                               memcpy(v, data, data_len);
+                               em->data = (unsigned long) v;
+                       }
+               }
+       }
+
+       em->matchid = em_hdr->matchid;
+       em->flags = em_hdr->flags;
+       em->datalen = data_len;
+
+       err = 0;
+errout:
+       return err;
+}
+
+/**
+ * tcf_em_tree_validate - validate ematch config TLV and build ematch tree
+ *
+ * @tp: classifier kind handle
+ * @rta: ematch tree configuration TLV
+ * @tree: destination ematch tree variable to store the resulting
+ *        ematch tree.
+ *
+ * This function validates the given configuration TLV @rta and builds an
+ * ematch tree in @tree. The resulting tree must later be copied into
+ * the private classifier data using tcf_em_tree_change(). You MUST NOT
+ * provide the ematch tree variable of the private classifier data directly,
+ * the changes would not be locked properly.
+ *
+ * Returns a negative error code if the configuration TLV contains errors.
+ */
+int tcf_em_tree_validate(struct tcf_proto *tp, struct rtattr *rta,
+                        struct tcf_ematch_tree *tree)
+{
+       int idx, list_len, matches_len, err = -EINVAL;
+       struct rtattr *tb[TCA_EMATCH_TREE_MAX];
+       struct rtattr *rt_match, *rt_hdr, *rt_list;
+       struct tcf_ematch_tree_hdr *tree_hdr;
+       struct tcf_ematch *em;
+
+       if (rtattr_parse_nested(tb, TCA_EMATCH_TREE_MAX, rta) < 0)
+               goto errout;
+
+       rt_hdr = tb[TCA_EMATCH_TREE_HDR-1];
+       rt_list = tb[TCA_EMATCH_TREE_LIST-1];
+
+       if (rt_hdr == NULL || rt_list == NULL)
+               goto errout;
+
+       if (RTA_PAYLOAD(rt_hdr) < sizeof(*tree_hdr) ||
+           RTA_PAYLOAD(rt_list) < sizeof(*rt_match))
+               goto errout;
+
+       tree_hdr = RTA_DATA(rt_hdr);
+       memcpy(&tree->hdr, tree_hdr, sizeof(*tree_hdr));
+
+       rt_match = RTA_DATA(rt_list);
+       list_len = RTA_PAYLOAD(rt_list);
+       matches_len = tree_hdr->nmatches * sizeof(*em);
+
+       tree->matches = kmalloc(matches_len, GFP_KERNEL);
+       if (tree->matches == NULL)
+               goto errout;
+       memset(tree->matches, 0, matches_len);
+
+       /* We do not use rtattr_parse_nested here because the maximum
+        * number of attributes is unknown. This saves us the allocation
+        * for a tb buffer which would serve no purpose at all.
+        * 
+        * The array of rt attributes is parsed in the order as they are
+        * provided, their type must be incremental from 1 to n. Even
+        * if it does not serve any real purpose, a failure of sticking
+        * to this policy will result in parsing failure. */
+       for (idx = 0; RTA_OK(rt_match, list_len); idx++) {
+               err = -EINVAL;
+
+               if (rt_match->rta_type != (idx + 1))
+                       goto errout_abort;
+
+               if (idx >= tree_hdr->nmatches)
+                       goto errout_abort;
+
+               if (RTA_PAYLOAD(rt_match) < sizeof(struct tcf_ematch_hdr))
+                       goto errout_abort;
+
+               em = tcf_em_get_match(tree, idx);
+
+               err = tcf_em_validate(tp, tree_hdr, em, rt_match, idx);
+               if (err < 0)
+                       goto errout_abort;
+
+               rt_match = RTA_NEXT(rt_match, list_len);
+       }
+
+       /* Check if the number of matches provided by userspace actually
+        * complies with the array of matches. The number was used for
+        * the validation of references and a mismatch could lead to
+        * undefined references during the matching process. */
+       if (idx != tree_hdr->nmatches) {
+               err = -EINVAL;
+               goto errout_abort;
+       }
+
+       err = 0;
+errout:
+       return err;
+
+errout_abort:
+       tcf_em_tree_destroy(tp, tree);
+       return err;
+}
+
+/**
+ * tcf_em_tree_destroy - destroy an ematch tree
+ *
+ * @tp: classifier kind handle
+ * @tree: ematch tree to be deleted
+ *
+ * This functions destroys an ematch tree previously created by
+ * tcf_em_tree_validate()/tcf_em_tree_change(). You must ensure that
+ * the ematch tree is not in use before calling this function.
+ */
+void tcf_em_tree_destroy(struct tcf_proto *tp, struct tcf_ematch_tree *tree)
+{
+       int i;
+
+       if (tree->matches == NULL)
+               return;
+
+       for (i = 0; i < tree->hdr.nmatches; i++) {
+               struct tcf_ematch *em = tcf_em_get_match(tree, i);
+
+               if (em->ops) {
+                       if (em->ops->destroy)
+                               em->ops->destroy(tp, em);
+                       else if (!tcf_em_is_simple(em) && em->data)
+                               kfree((void *) em->data);
+                       module_put(em->ops->owner);
+               }
+       }
+       
+       tree->hdr.nmatches = 0;
+       kfree(tree->matches);
+}
+
+/**
+ * tcf_em_tree_dump - dump ematch tree into a rtnl message
+ *
+ * @skb: skb holding the rtnl message
+ * @t: ematch tree to be dumped
+ * @tlv: TLV type to be used to encapsulate the tree
+ *
+ * This function dumps a ematch tree into a rtnl message. It is valid to
+ * call this function while the ematch tree is in use.
+ *
+ * Returns -1 if the skb tailroom is insufficient.
+ */
+int tcf_em_tree_dump(struct sk_buff *skb, struct tcf_ematch_tree *tree, int tlv)
+{
+       int i;
+       struct rtattr * top_start = (struct rtattr*) skb->tail;
+       struct rtattr * list_start;
+
+       RTA_PUT(skb, tlv, 0, NULL);
+       RTA_PUT(skb, TCA_EMATCH_TREE_HDR, sizeof(tree->hdr), &tree->hdr);
+
+       list_start = (struct rtattr *) skb->tail;
+       RTA_PUT(skb, TCA_EMATCH_TREE_LIST, 0, NULL);
+
+       for (i = 0; i < tree->hdr.nmatches; i++) {
+               struct rtattr *match_start = (struct rtattr*) skb->tail;
+               struct tcf_ematch *em = tcf_em_get_match(tree, i);
+               struct tcf_ematch_hdr em_hdr = {
+                       .kind = em->ops ? em->ops->kind : TCF_EM_CONTAINER,
+                       .matchid = em->matchid,
+                       .flags = em->flags
+               };
+
+               RTA_PUT(skb, i+1, sizeof(em_hdr), &em_hdr);
+
+               if (em->ops && em->ops->dump) {
+                       if (em->ops->dump(skb, em) < 0)
+                               goto rtattr_failure;
+               } else if (tcf_em_is_container(em) || tcf_em_is_simple(em)) {
+                       u32 u = em->data;
+                       RTA_PUT_NOHDR(skb, sizeof(u), &u);
+               } else if (em->datalen > 0)
+                       RTA_PUT_NOHDR(skb, em->datalen, (void *) em->data);
+
+               match_start->rta_len = skb->tail - (u8*) match_start;
+       }
+
+       list_start->rta_len = skb->tail - (u8 *) list_start;
+       top_start->rta_len = skb->tail - (u8 *) top_start;
+
+       return 0;
+
+rtattr_failure:
+       return -1;
+}
+
+static inline int tcf_em_match(struct sk_buff *skb, struct tcf_ematch *em,
+                              struct tcf_pkt_info *info)
+{
+       int r = em->ops->match(skb, em, info);
+       return tcf_em_is_inverted(em) ? !r : r;
+}
+
+/* Do not use this function directly, use tcf_em_tree_match instead */
+int __tcf_em_tree_match(struct sk_buff *skb, struct tcf_ematch_tree *tree,
+                       struct tcf_pkt_info *info)
+{
+       int stackp = 0, match_idx = 0, res = 0;
+       struct tcf_ematch *cur_match;
+       int stack[CONFIG_NET_EMATCH_STACK];
+
+proceed:
+       while (match_idx < tree->hdr.nmatches) {
+               cur_match = tcf_em_get_match(tree, match_idx);
+
+               if (tcf_em_is_container(cur_match)) {
+                       if (unlikely(stackp >= CONFIG_NET_EMATCH_STACK))
+                               goto stack_overflow;
+
+                       stack[stackp++] = match_idx;
+                       match_idx = cur_match->data;
+                       goto proceed;
+               }
+
+               res = tcf_em_match(skb, cur_match, info);
+
+               if (tcf_em_early_end(cur_match, res))
+                       break;
+
+               match_idx++;
+       }
+
+pop_stack:
+       if (stackp > 0) {
+               match_idx = stack[--stackp];
+               cur_match = tcf_em_get_match(tree, match_idx);
+
+               if (tcf_em_early_end(cur_match, res))
+                       goto pop_stack;
+               else {
+                       match_idx++;
+                       goto proceed;
+               }
+       }
+
+       return res;
+
+stack_overflow:
+       if (net_ratelimit())
+               printk("Local stack overflow, increase NET_EMATCH_STACK\n");
+       return -1;
+}
+
+EXPORT_SYMBOL(tcf_em_register);
+EXPORT_SYMBOL(tcf_em_unregister);
+EXPORT_SYMBOL(tcf_em_tree_validate);
+EXPORT_SYMBOL(tcf_em_tree_destroy);
+EXPORT_SYMBOL(tcf_em_tree_dump);
+EXPORT_SYMBOL(__tcf_em_tree_match);
diff --git a/net/sched/simple.c b/net/sched/simple.c
new file mode 100644 (file)
index 0000000..3ab4c67
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * net/sched/simp.c    Simple example of an action
+ *
+ *             This program is free software; you can redistribute it and/or
+ *             modify it under the terms of the 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 (2005)
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <net/pkt_sched.h>
+
+#define TCA_ACT_SIMP 22
+
+/* XXX: Hide all these common elements under some macro 
+ * probably
+*/
+#include <linux/tc_act/tc_defact.h>
+#include <net/tc_act/tc_defact.h>
+
+/* use generic hash table with 8 buckets */
+#define MY_TAB_SIZE     8
+#define MY_TAB_MASK     (MY_TAB_SIZE - 1)
+static u32 idx_gen;
+static struct tcf_defact *tcf_simp_ht[MY_TAB_SIZE];
+static DEFINE_RWLOCK(simp_lock);
+
+/* override the defaults */
+#define tcf_st         tcf_defact
+#define tc_st          tc_defact
+#define tcf_t_lock     simp_lock
+#define tcf_ht         tcf_simp_ht
+
+#define CONFIG_NET_ACT_INIT 1
+#include <net/pkt_act.h>
+#include <net/act_generic.h>
+
+static int tcf_simp(struct sk_buff **pskb, struct tc_action *a)
+{
+       struct sk_buff *skb = *pskb;
+       struct tcf_defact *p = PRIV(a, defact);
+
+       spin_lock(&p->lock);
+       p->tm.lastuse = jiffies;
+       p->bstats.bytes += skb->len;
+       p->bstats.packets++;
+
+       /* print policy string followed by _ then packet count 
+        * Example if this was the 3rd packet and the string was "hello" 
+        * then it would look like "hello_3" (without quotes) 
+        **/
+       printk("simple: %s_%d\n", (char *)p->defdata, p->bstats.packets);
+       spin_unlock(&p->lock);
+       return p->action;
+}
+
+static struct tc_action_ops act_simp_ops = {
+       .kind = "simple",
+       .type = TCA_ACT_SIMP,
+       .capab = TCA_CAP_NONE,
+       .owner = THIS_MODULE,
+       .act = tcf_simp,
+       tca_use_default_ops
+};
+
+MODULE_AUTHOR("Jamal Hadi Salim(2005)");
+MODULE_DESCRIPTION("Simple example action");
+MODULE_LICENSE("GPL");
+
+static int __init simp_init_module(void)
+{
+       int ret = tcf_register_action(&act_simp_ops);
+       if (!ret)
+               printk("Simple TC action Loaded\n");
+       return ret;
+}
+
+static void __exit simp_cleanup_module(void)
+{
+       tcf_unregister_action(&act_simp_ops);
+}
+
+module_init(simp_init_module);
+module_exit(simp_cleanup_module);
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
new file mode 100644 (file)
index 0000000..7fdabea
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * compat ioctls for control API
+ *
+ *   Copyright (c) by 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
+ */
+
+/* this file included from control.c */
+
+#include <linux/compat.h>
+
+struct sndrv_ctl_elem_list32 {
+       u32 offset;
+       u32 space;
+       u32 used;
+       u32 count;
+       u32 pids;
+       unsigned char reserved[50];
+} /* don't set packed attribute here */;
+
+static int snd_ctl_elem_list_compat(snd_card_t *card, struct sndrv_ctl_elem_list32 __user *data32)
+{
+       struct sndrv_ctl_elem_list __user *data;
+       compat_caddr_t ptr;
+       int err;
+
+       data = compat_alloc_user_space(sizeof(*data));
+
+       /* offset, space, used, count */
+       if (copy_in_user(data, data32, 4 * sizeof(u32)))
+               return -EFAULT;
+       /* pids */
+       if (get_user(ptr, &data32->pids) ||
+           put_user(compat_ptr(ptr), &data->pids))
+               return -EFAULT;
+       err = snd_ctl_elem_list(card, data);
+       if (err < 0)
+               return err;
+       /* copy the result */
+       if (copy_in_user(data32, data, 4 * sizeof(u32)))
+               return -EFAULT;
+       return 0;
+}
+
+/*
+ * control element info
+ * it uses union, so the things are not easy..
+ */
+
+struct sndrv_ctl_elem_info32 {
+       struct sndrv_ctl_elem_id id; // the size of struct is same
+       s32 type;
+       u32 access;
+       u32 count;
+       s32 owner;
+       union {
+               struct {
+                       s32 min;
+                       s32 max;
+                       s32 step;
+               } integer;
+               struct {
+                       u64 min;
+                       u64 max;
+                       u64 step;
+               } integer64;
+               struct {
+                       u32 items;
+                       u32 item;
+                       char name[64];
+               } enumerated;
+               unsigned char reserved[128];
+       } value;
+       unsigned char reserved[64];
+} __attribute__((packed));
+
+static int snd_ctl_elem_info_compat(snd_ctl_file_t *ctl, struct sndrv_ctl_elem_info32 __user *data32)
+{
+       struct sndrv_ctl_elem_info *data;
+       int err;
+
+       data = kcalloc(1, sizeof(*data), GFP_KERNEL);
+       if (! data)
+               return -ENOMEM;
+
+       err = -EFAULT;
+       /* copy id */
+       if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
+               goto error;
+       /* we need to copy the item index.
+        * hope this doesn't break anything..
+        */
+       if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
+               goto error;
+       err = snd_ctl_elem_info(ctl, data);
+       if (err < 0)
+               goto error;
+       /* restore info to 32bit */
+       err = -EFAULT;
+       /* id, type, access, count */
+       if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) ||
+           copy_to_user(&data32->type, &data->type, 3 * sizeof(u32)))
+               goto error;
+       if (put_user(data->owner, &data32->owner))
+               goto error;
+       switch (data->type) {
+       case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+       case SNDRV_CTL_ELEM_TYPE_INTEGER:
+               if (put_user(data->value.integer.min, &data32->value.integer.min) ||
+                   put_user(data->value.integer.max, &data32->value.integer.max) ||
+                   put_user(data->value.integer.step, &data32->value.integer.step))
+                       goto error;
+               break;
+       case SNDRV_CTL_ELEM_TYPE_INTEGER64:
+               if (copy_to_user(&data32->value.integer64,
+                                &data->value.integer64,
+                                sizeof(data->value.integer64)))
+                       goto error;
+               break;
+       case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+               if (copy_to_user(&data32->value.enumerated,
+                                &data->value.enumerated,
+                                sizeof(data->value.enumerated)))
+                       goto error;
+               break;
+       default:
+               break;
+       }
+       err = 0;
+ error:
+       kfree(data);
+       return err;
+}
+
+/* read / write */
+struct sndrv_ctl_elem_value32 {
+       struct sndrv_ctl_elem_id id;
+       unsigned int indirect;  /* bit-field causes misalignment */
+        union {
+               s32 integer[128];
+               unsigned char data[512];
+#ifndef CONFIG_X86_64
+               s64 integer64[64];
+#endif
+        } value;
+        unsigned char reserved[128];
+};
+
+
+/* get the value type and count of the control */
+static int get_ctl_type(snd_card_t *card, snd_ctl_elem_id_t *id, int *countp)
+{
+       snd_kcontrol_t *kctl;
+       snd_ctl_elem_info_t info;
+       int err;
+
+       down_read(&card->controls_rwsem);
+       kctl = snd_ctl_find_id(card, id);
+       if (! kctl) {
+               up_read(&card->controls_rwsem);
+               return -ENXIO;
+       }
+       info.id = *id;
+       err = kctl->info(kctl, &info);
+       up_read(&card->controls_rwsem);
+       if (err >= 0) {
+               err = info.type;
+               *countp = info.count;
+       }
+       return err;
+}
+
+static int get_elem_size(int type, int count)
+{
+       switch (type) {
+       case SNDRV_CTL_ELEM_TYPE_INTEGER64:
+               return sizeof(s64) * count;
+       case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+               return sizeof(int) * count;
+       case SNDRV_CTL_ELEM_TYPE_BYTES:
+               return 512;
+       case SNDRV_CTL_ELEM_TYPE_IEC958:
+               return sizeof(struct sndrv_aes_iec958);
+       default:
+               return -1;
+       }
+}
+
+static int copy_ctl_value_from_user(snd_card_t *card,
+                                   struct sndrv_ctl_elem_value *data,
+                                   struct sndrv_ctl_elem_value32 __user *data32,
+                                   int *typep, int *countp)
+{
+       int i, type, count, size;
+       unsigned int indirect;
+
+       if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
+               return -EFAULT;
+       if (get_user(indirect, &data32->indirect))
+               return -EFAULT;
+       if (indirect)
+               return -EINVAL;
+       type = get_ctl_type(card, &data->id, &count);
+       if (type < 0)
+               return type;
+
+       if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
+           type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
+               for (i = 0; i < count; i++) {
+                       int val;
+                       if (get_user(val, &data32->value.integer[i]))
+                               return -EFAULT;
+                       data->value.integer.value[i] = val;
+               }
+       } else {
+               size = get_elem_size(type, count);
+               if (size < 0) {
+                       printk(KERN_ERR "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
+                       return -EINVAL;
+               }
+               if (copy_from_user(data->value.bytes.data,
+                                  data32->value.data, size))
+                       return -EFAULT;
+       }
+
+       *typep = type;
+       *countp = count;
+       return 0;
+}
+
+/* restore the value to 32bit */
+static int copy_ctl_value_to_user(struct sndrv_ctl_elem_value32 __user *data32,
+                                 struct sndrv_ctl_elem_value *data,
+                                 int type, int count)
+{
+       int i, size;
+
+       if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
+           type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
+               for (i = 0; i < count; i++) {
+                       int val;
+                       val = data->value.integer.value[i];
+                       if (put_user(val, &data32->value.integer[i]))
+                               return -EFAULT;
+               }
+       } else {
+               size = get_elem_size(type, count);
+               if (copy_to_user(data32->value.data,
+                                data->value.bytes.data, size))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+static int snd_ctl_elem_read_user_compat(snd_card_t *card, 
+                                        struct sndrv_ctl_elem_value32 __user *data32)
+{
+       struct sndrv_ctl_elem_value *data;
+       int err, type, count;
+
+       data = kcalloc(1, sizeof(*data), GFP_KERNEL);
+       if (data == NULL)
+               return -ENOMEM;
+
+       if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0)
+               goto error;
+       if ((err = snd_ctl_elem_read(card, data)) < 0)
+               goto error;
+       err = copy_ctl_value_to_user(data32, data, type, count);
+ error:
+       kfree(data);
+       return err;
+}
+
+static int snd_ctl_elem_write_user_compat(snd_ctl_file_t *file,
+                                         struct sndrv_ctl_elem_value32 __user *data32)
+{
+       struct sndrv_ctl_elem_value *data;
+       int err, type, count;
+
+       data = kcalloc(1, sizeof(*data), GFP_KERNEL);
+       if (data == NULL)
+               return -ENOMEM;
+
+       if ((err = copy_ctl_value_from_user(file->card, data, data32, &type, &count)) < 0)
+               goto error;
+       if ((err = snd_ctl_elem_write(file->card, file, data)) < 0)
+               goto error;
+       err = copy_ctl_value_to_user(data32, data, type, count);
+ error:
+       kfree(data);
+       return err;
+}
+
+/* add or replace a user control */
+static int snd_ctl_elem_add_compat(snd_ctl_file_t *file,
+                                  struct sndrv_ctl_elem_info32 __user *data32,
+                                  int replace)
+{
+       struct sndrv_ctl_elem_info *data;
+       int err;
+
+       data = kcalloc(1, sizeof(*data), GFP_KERNEL);
+       if (! data)
+               return -ENOMEM;
+
+       err = -EFAULT;
+       /* id, type, access, count */ \
+       if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
+           copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
+               goto error;
+       if (get_user(data->owner, &data32->owner) ||
+           get_user(data->type, &data32->type))
+               goto error;
+       switch (data->type) {
+       case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+       case SNDRV_CTL_ELEM_TYPE_INTEGER:
+               if (get_user(data->value.integer.min, &data32->value.integer.min) ||
+                   get_user(data->value.integer.max, &data32->value.integer.max) ||
+                   get_user(data->value.integer.step, &data32->value.integer.step))
+                       goto error;
+               break;
+       case SNDRV_CTL_ELEM_TYPE_INTEGER64:
+               if (copy_from_user(&data->value.integer64,
+                                  &data32->value.integer64,
+                                  sizeof(data->value.integer64)))
+                       goto error;
+               break;
+       case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
+               if (copy_from_user(&data->value.enumerated,
+                                  &data32->value.enumerated,
+                                  sizeof(data->value.enumerated)))
+                       goto error;
+               break;
+       default:
+               break;
+       }
+       err = snd_ctl_elem_add(file, data, replace);
+ error:
+       kfree(data);
+       return err;
+}  
+
+enum {
+       SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct sndrv_ctl_elem_list32),
+       SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct sndrv_ctl_elem_info32),
+       SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct sndrv_ctl_elem_value32),
+       SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct sndrv_ctl_elem_value32),
+       SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct sndrv_ctl_elem_info32),
+       SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct sndrv_ctl_elem_info32),
+};
+
+static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       snd_ctl_file_t *ctl;
+       struct list_head *list;
+       void __user *argp = compat_ptr(arg);
+       int err;
+
+       ctl = file->private_data;
+       snd_assert(ctl && ctl->card, return -ENXIO);
+
+       switch (cmd) {
+       case SNDRV_CTL_IOCTL_PVERSION:
+       case SNDRV_CTL_IOCTL_CARD_INFO:
+       case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
+       case SNDRV_CTL_IOCTL_POWER:
+       case SNDRV_CTL_IOCTL_POWER_STATE:
+       case SNDRV_CTL_IOCTL_ELEM_LOCK:
+       case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
+               return snd_ctl_ioctl(file, cmd, (unsigned long)argp);
+       case SNDRV_CTL_IOCTL_ELEM_LIST32:
+               return snd_ctl_elem_list_compat(ctl->card, argp);
+       case SNDRV_CTL_IOCTL_ELEM_INFO32:
+               return snd_ctl_elem_info_compat(ctl, argp);
+       case SNDRV_CTL_IOCTL_ELEM_READ32:
+               return snd_ctl_elem_read_user_compat(ctl->card, argp);
+       case SNDRV_CTL_IOCTL_ELEM_WRITE32:
+               return snd_ctl_elem_write_user_compat(ctl, argp);
+       case SNDRV_CTL_IOCTL_ELEM_ADD32:
+               return snd_ctl_elem_add_compat(ctl, argp, 0);
+       case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
+               return snd_ctl_elem_add_compat(ctl, argp, 1);
+       }
+
+       down_read(&snd_ioctl_rwsem);
+       list_for_each(list, &snd_control_compat_ioctls) {
+               snd_kctl_ioctl_t *p = list_entry(list, snd_kctl_ioctl_t, list);
+               if (p->fioctl) {
+                       err = p->fioctl(ctl->card, ctl, cmd, arg);
+                       if (err != -ENOIOCTLCMD) {
+                               up_read(&snd_ioctl_rwsem);
+                               return err;
+                       }
+               }
+       }
+       up_read(&snd_ioctl_rwsem);
+       return -ENOIOCTLCMD;
+}
diff --git a/sound/core/hwdep_compat.c b/sound/core/hwdep_compat.c
new file mode 100644 (file)
index 0000000..6866f42
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ *   32bit -> 64bit ioctl wrapper for hwdep API
+ *   Copyright (c) by 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
+ *
+ */
+
+/* This file is included from hwdep.c */
+
+#include <linux/compat.h>
+
+struct sndrv_hwdep_dsp_image32 {
+       u32 index;
+       unsigned char name[64];
+       u32 image;      /* pointer */
+       u32 length;
+       u32 driver_data;
+} /* don't set packed attribute here */;
+
+static int snd_hwdep_dsp_load_compat(snd_hwdep_t *hw,
+                                    struct sndrv_hwdep_dsp_image32 __user *src)
+{
+       struct sndrv_hwdep_dsp_image *dst;
+       compat_caddr_t ptr;
+       u32 val;
+
+       dst = compat_alloc_user_space(sizeof(*dst));
+
+       /* index and name */
+       if (copy_in_user(dst, src, 4 + 64))
+               return -EFAULT;
+       if (get_user(ptr, &src->image) ||
+           put_user(compat_ptr(ptr), &dst->image))
+               return -EFAULT;
+       if (get_user(val, &src->length) ||
+           put_user(val, &dst->length))
+               return -EFAULT;
+       if (get_user(val, &src->driver_data) ||
+           put_user(val, &dst->driver_data))
+               return -EFAULT;
+
+       return snd_hwdep_dsp_load(hw, dst);
+}
+
+enum {
+       SNDRV_HWDEP_IOCTL_DSP_LOAD32   = _IOW('H', 0x03, struct sndrv_hwdep_dsp_image32)
+};
+
+static long snd_hwdep_ioctl_compat(struct file * file, unsigned int cmd, unsigned long arg)
+{
+       snd_hwdep_t *hw = file->private_data;
+       void __user *argp = compat_ptr(arg);
+       switch (cmd) {
+       case SNDRV_HWDEP_IOCTL_PVERSION:
+       case SNDRV_HWDEP_IOCTL_INFO:
+       case SNDRV_HWDEP_IOCTL_DSP_STATUS:
+               return snd_hwdep_ioctl(file, cmd, (unsigned long)argp);
+       case SNDRV_HWDEP_IOCTL_DSP_LOAD32:
+               return snd_hwdep_dsp_load_compat(hw, argp);
+       }
+       if (hw->ops.ioctl_compat)
+               return hw->ops.ioctl_compat(hw, file, cmd, arg);
+       return -ENOIOCTLCMD;
+}
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
new file mode 100644 (file)
index 0000000..3920bf0
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ *   32bit -> 64bit ioctl wrapper for PCM API
+ *   Copyright (c) by 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
+ *
+ */
+
+/* This file included from pcm_native.c */
+
+#include <linux/compat.h>
+
+static int snd_pcm_ioctl_delay_compat(snd_pcm_substream_t *substream,
+                                     s32 __user *src)
+{
+       snd_pcm_sframes_t delay;
+       mm_segment_t fs;
+       int err;
+
+       fs = snd_enter_user();
+       err = snd_pcm_delay(substream, &delay);
+       snd_leave_user(fs);
+       if (err < 0)
+               return err;
+       if (put_user(delay, src))
+               return -EFAULT;
+       return err;
+}
+
+static int snd_pcm_ioctl_rewind_compat(snd_pcm_substream_t *substream,
+                                      u32 __user *src)
+{
+       snd_pcm_uframes_t frames;
+       int err;
+
+       if (get_user(frames, src))
+               return -EFAULT;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               err = snd_pcm_playback_rewind(substream, frames);
+       else
+               err = snd_pcm_capture_rewind(substream, frames);
+       if (put_user(err, src))
+               return -EFAULT;
+       return err < 0 ? err : 0;
+}
+
+static int snd_pcm_ioctl_forward_compat(snd_pcm_substream_t *substream,
+                                      u32 __user *src)
+{
+       snd_pcm_uframes_t frames;
+       int err;
+
+       if (get_user(frames, src))
+               return -EFAULT;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               err = snd_pcm_playback_forward(substream, frames);
+       else
+               err = snd_pcm_capture_forward(substream, frames);
+       if (put_user(err, src))
+               return -EFAULT;
+       return err < 0 ? err : 0;
+}
+
+struct sndrv_pcm_hw_params32 {
+       u32 flags;
+       struct sndrv_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; /* this must be identical */
+       struct sndrv_mask mres[5];      /* reserved masks */
+       struct sndrv_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1];
+       struct sndrv_interval ires[9];  /* reserved intervals */
+       u32 rmask;
+       u32 cmask;
+       u32 info;
+       u32 msbits;
+       u32 rate_num;
+       u32 rate_den;
+       u32 fifo_size;
+       unsigned char reserved[64];
+};
+
+struct sndrv_pcm_sw_params32 {
+       s32 tstamp_mode;
+       u32 period_step;
+       u32 sleep_min;
+       u32 avail_min;
+       u32 xfer_align;
+       u32 start_threshold;
+       u32 stop_threshold;
+       u32 silence_threshold;
+       u32 silence_size;
+       u32 boundary;
+       unsigned char reserved[64];
+};
+
+static int snd_pcm_ioctl_sw_params_compat(snd_pcm_substream_t *substream,
+                                         struct sndrv_pcm_sw_params32 __user *src)
+{
+       snd_pcm_sw_params_t params;
+       int err;
+
+       memset(&params, 0, sizeof(params));
+       if (get_user(params.tstamp_mode, &src->tstamp_mode) ||
+           get_user(params.period_step, &src->period_step) ||
+           get_user(params.sleep_min, &src->sleep_min) ||
+           get_user(params.avail_min, &src->avail_min) ||
+           get_user(params.xfer_align, &src->xfer_align) ||
+           get_user(params.start_threshold, &src->start_threshold) ||
+           get_user(params.stop_threshold, &src->stop_threshold) ||
+           get_user(params.silence_threshold, &src->silence_threshold) ||
+           get_user(params.silence_size, &src->silence_size))
+               return -EFAULT;
+       err = snd_pcm_sw_params(substream, &params);
+       if (err < 0)
+               return err;
+       if (put_user(params.boundary, &src->boundary))
+               return -EFAULT;
+       return err;
+}
+
+struct sndrv_pcm_channel_info32 {
+       u32 channel;
+       u32 offset;
+       u32 first;
+       u32 step;
+};
+
+static int snd_pcm_ioctl_channel_info_compat(snd_pcm_substream_t *substream,
+                                            struct sndrv_pcm_channel_info32 __user *src)
+{
+       snd_pcm_channel_info_t info;
+       int err;
+
+       if (get_user(info.channel, &src->channel) ||
+           get_user(info.offset, &src->offset) ||
+           get_user(info.first, &src->first) ||
+           get_user(info.step, &src->step))
+               return -EFAULT;
+       err = snd_pcm_channel_info(substream, &info);
+       if (err < 0)
+               return err;
+       if (put_user(info.channel, &src->channel) ||
+           put_user(info.offset, &src->offset) ||
+           put_user(info.first, &src->first) ||
+           put_user(info.step, &src->step))
+               return -EFAULT;
+       return err;
+}
+
+struct sndrv_pcm_status32 {
+       s32 state;
+       struct compat_timespec trigger_tstamp;
+       struct compat_timespec tstamp;
+       u32 appl_ptr;
+       u32 hw_ptr;
+       s32 delay;
+       u32 avail;
+       u32 avail_max;
+       u32 overrange;
+       s32 suspended_state;
+       unsigned char reserved[60];
+} __attribute__((packed));
+
+
+static int snd_pcm_status_user_compat(snd_pcm_substream_t *substream,
+                                     struct sndrv_pcm_status32 __user *src)
+{
+       snd_pcm_status_t status;
+       int err;
+
+       err = snd_pcm_status(substream, &status);
+       if (err < 0)
+               return err;
+
+       if (put_user(status.state, &src->state) ||
+           put_user(status.trigger_tstamp.tv_sec, &src->trigger_tstamp.tv_sec) ||
+           put_user(status.trigger_tstamp.tv_nsec, &src->trigger_tstamp.tv_nsec) ||
+           put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) ||
+           put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) ||
+           put_user(status.appl_ptr, &src->appl_ptr) ||
+           put_user(status.hw_ptr, &src->hw_ptr) ||
+           put_user(status.delay, &src->delay) ||
+           put_user(status.avail, &src->avail) ||
+           put_user(status.avail_max, &src->avail_max) ||
+           put_user(status.overrange, &src->overrange) ||
+           put_user(status.suspended_state, &src->suspended_state))
+               return -EFAULT;
+
+       return err;
+}
+
+/* recalcuate the boundary within 32bit */
+static void recalculate_boundary(snd_pcm_runtime_t *runtime)
+{
+       if (! runtime->buffer_size)
+               return;
+       runtime->boundary = runtime->buffer_size;
+       while (runtime->boundary * 2 <= 0x7fffffffUL - runtime->buffer_size)
+               runtime->boundary *= 2;
+}
+
+/* both for HW_PARAMS and HW_REFINE */
+static int snd_pcm_ioctl_hw_params_compat(snd_pcm_substream_t *substream,
+                                         int refine, 
+                                         struct sndrv_pcm_hw_params32 __user *data32)
+{
+       struct sndrv_pcm_hw_params *data;
+       snd_pcm_runtime_t *runtime;
+       int err;
+
+       if (! (runtime = substream->runtime))
+               return -ENOTTY;
+
+       data = kmalloc(sizeof(*data), GFP_KERNEL);
+       if (data == NULL)
+               return -ENOMEM;
+       /* only fifo_size is different, so just copy all */
+       if (copy_from_user(data, data32, sizeof(*data32))) {
+               err = -EFAULT;
+               goto error;
+       }
+       if (refine)
+               err = snd_pcm_hw_refine(substream, data);
+       else
+               err = snd_pcm_hw_params(substream, data);
+       if (err < 0)
+               goto error;
+       if (copy_to_user(data32, data, sizeof(*data32)) ||
+           put_user(data->fifo_size, &data32->fifo_size)) {
+               err = -EFAULT;
+               goto error;
+       }
+
+       if (! refine)
+               recalculate_boundary(runtime);
+ error:
+       kfree(data);
+       return err;
+}
+
+
+/*
+ */
+struct sndrv_xferi32 {
+       s32 result;
+       u32 buf;
+       u32 frames;
+};
+
+static int snd_pcm_ioctl_xferi_compat(snd_pcm_substream_t *substream,
+                                     int dir, struct sndrv_xferi32 __user *data32)
+{
+       compat_caddr_t buf;
+       u32 frames;
+       int err;
+
+       if (! substream->runtime)
+               return -ENOTTY;
+       if (substream->stream != dir)
+               return -EINVAL;
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+               return -EBADFD;
+
+       if (get_user(buf, &data32->buf) ||
+           get_user(frames, &data32->frames))
+               return -EFAULT;
+
+       if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+               err = snd_pcm_lib_write(substream, compat_ptr(buf), frames);
+       else
+               err = snd_pcm_lib_read(substream, compat_ptr(buf), frames);
+       if (err < 0)
+               return err;
+       /* copy the result */
+       if (put_user(err, &data32->result))
+               return -EFAULT;
+       return 0;
+}
+
+
+/* snd_xfern needs remapping of bufs */
+struct sndrv_xfern32 {
+       s32 result;
+       u32 bufs;  /* this is void **; */
+       u32 frames;
+};
+
+/*
+ * xfern ioctl nees to copy (up to) 128 pointers on stack.
+ * although we may pass the copied pointers through f_op->ioctl, but the ioctl
+ * handler there expands again the same 128 pointers on stack, so it is better
+ * to handle the function (calling pcm_readv/writev) directly in this handler.
+ */
+static int snd_pcm_ioctl_xfern_compat(snd_pcm_substream_t *substream,
+                                     int dir, struct sndrv_xfern32 __user *data32)
+{
+       compat_caddr_t buf;
+       compat_caddr_t __user *bufptr;
+       u32 frames;
+       void __user **bufs;
+       int err, ch, i;
+
+       if (! substream->runtime)
+               return -ENOTTY;
+       if (substream->stream != dir)
+               return -EINVAL;
+
+       if ((ch = substream->runtime->channels) > 128)
+               return -EINVAL;
+       if (get_user(buf, &data32->bufs) ||
+           get_user(frames, &data32->frames))
+               return -EFAULT;
+       bufptr = compat_ptr(buf);
+       bufs = kmalloc(sizeof(void __user *) * ch, GFP_KERNEL);
+       if (bufs == NULL)
+               return -ENOMEM;
+       for (i = 0; i < ch; i++) {
+               u32 ptr;
+               if (get_user(ptr, bufptr)) {
+                       kfree(bufs);
+                       return -EFAULT;
+               }
+               bufs[ch] = compat_ptr(ptr);
+               bufptr++;
+       }
+       if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+               err = snd_pcm_lib_writev(substream, bufs, frames);
+       else
+               err = snd_pcm_lib_readv(substream, bufs, frames);
+       if (err >= 0) {
+               if (put_user(err, &data32->result))
+                       err = -EFAULT;
+       }
+       kfree(bufs);
+       return err;
+}
+
+
+struct sndrv_pcm_mmap_status32 {
+       s32 state;
+       s32 pad1;
+       u32 hw_ptr;
+       struct compat_timespec tstamp;
+       s32 suspended_state;
+} __attribute__((packed));
+
+struct sndrv_pcm_mmap_control32 {
+       u32 appl_ptr;
+       u32 avail_min;
+};
+
+struct sndrv_pcm_sync_ptr32 {
+       u32 flags;
+       union {
+               struct sndrv_pcm_mmap_status32 status;
+               unsigned char reserved[64];
+       } s;
+       union {
+               struct sndrv_pcm_mmap_control32 control;
+               unsigned char reserved[64];
+       } c;
+} __attribute__((packed));
+
+static int snd_pcm_ioctl_sync_ptr_compat(snd_pcm_substream_t *substream,
+                                        struct sndrv_pcm_sync_ptr32 __user *src)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       volatile struct sndrv_pcm_mmap_status *status;
+       volatile struct sndrv_pcm_mmap_control *control;
+       u32 sflags;
+       struct sndrv_pcm_mmap_control scontrol;
+       struct sndrv_pcm_mmap_status sstatus;
+       int err;
+
+       snd_assert(runtime, return -EINVAL);
+
+       if (get_user(sflags, &src->flags) ||
+           get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
+           get_user(scontrol.avail_min, &src->c.control.avail_min))
+               return -EFAULT;
+       if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) {
+               err = snd_pcm_hwsync(substream);
+               if (err < 0)
+                       return err;
+       }
+       status = runtime->status;
+       control = runtime->control;
+       snd_pcm_stream_lock_irq(substream);
+       if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+               control->appl_ptr = scontrol.appl_ptr;
+       else
+               scontrol.appl_ptr = control->appl_ptr;
+       if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN))
+               control->avail_min = scontrol.avail_min;
+       else
+               scontrol.avail_min = control->avail_min;
+       sstatus.state = status->state;
+       sstatus.hw_ptr = status->hw_ptr;
+       sstatus.tstamp = status->tstamp;
+       sstatus.suspended_state = status->suspended_state;
+       snd_pcm_stream_unlock_irq(substream);
+       if (put_user(sstatus.state, &src->s.status.state) ||
+           put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
+           put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp.tv_sec) ||
+           put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp.tv_nsec) ||
+           put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
+           put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
+           put_user(scontrol.avail_min, &src->c.control.avail_min))
+               return -EFAULT;
+
+       return 0;
+}
+
+
+/*
+ */
+enum {
+       SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct sndrv_pcm_hw_params32),
+       SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct sndrv_pcm_hw_params32),
+       SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct sndrv_pcm_sw_params32),
+       SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct sndrv_pcm_status32),
+       SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32),
+       SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct sndrv_pcm_channel_info32),
+       SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32),
+       SNDRV_PCM_IOCTL_FORWARD32 = _IOW('A', 0x49, u32),
+       SNDRV_PCM_IOCTL_WRITEI_FRAMES32 = _IOW('A', 0x50, struct sndrv_xferi32),
+       SNDRV_PCM_IOCTL_READI_FRAMES32 = _IOR('A', 0x51, struct sndrv_xferi32),
+       SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct sndrv_xfern32),
+       SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct sndrv_xfern32),
+       SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct sndrv_pcm_sync_ptr32),
+
+};
+
+static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       snd_pcm_file_t *pcm_file;
+       snd_pcm_substream_t *substream;
+       void __user *argp = compat_ptr(arg);
+
+       pcm_file = file->private_data;
+       if (! pcm_file)
+               return -ENOTTY;
+       substream = pcm_file->substream;
+       if (! substream)
+               return -ENOTTY;
+
+       /*
+        * When PCM is used on 32bit mode, we need to disable
+        * mmap of PCM status/control records because of the size
+        * incompatibility.
+        */
+       substream->no_mmap_ctrl = 1;
+
+       switch (cmd) {
+       case SNDRV_PCM_IOCTL_PVERSION:
+       case SNDRV_PCM_IOCTL_INFO:
+       case SNDRV_PCM_IOCTL_TSTAMP:
+       case SNDRV_PCM_IOCTL_HWSYNC:
+       case SNDRV_PCM_IOCTL_PREPARE:
+       case SNDRV_PCM_IOCTL_RESET:
+       case SNDRV_PCM_IOCTL_START:
+       case SNDRV_PCM_IOCTL_DROP:
+       case SNDRV_PCM_IOCTL_DRAIN:
+       case SNDRV_PCM_IOCTL_PAUSE:
+       case SNDRV_PCM_IOCTL_HW_FREE:
+       case SNDRV_PCM_IOCTL_RESUME:
+       case SNDRV_PCM_IOCTL_XRUN:
+       case SNDRV_PCM_IOCTL_LINK:
+       case SNDRV_PCM_IOCTL_UNLINK:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       return snd_pcm_playback_ioctl1(substream, cmd, argp);
+               else
+                       return snd_pcm_capture_ioctl1(substream, cmd, argp);
+       case SNDRV_PCM_IOCTL_HW_REFINE32:
+               return snd_pcm_ioctl_hw_params_compat(substream, 1, argp);
+       case SNDRV_PCM_IOCTL_HW_PARAMS32:
+               return snd_pcm_ioctl_hw_params_compat(substream, 0, argp);
+       case SNDRV_PCM_IOCTL_SW_PARAMS32:
+               return snd_pcm_ioctl_sw_params_compat(substream, argp);
+       case SNDRV_PCM_IOCTL_STATUS32:
+               return snd_pcm_status_user_compat(substream, argp);
+       case SNDRV_PCM_IOCTL_SYNC_PTR32:
+               return snd_pcm_ioctl_sync_ptr_compat(substream, argp);
+       case SNDRV_PCM_IOCTL_CHANNEL_INFO32:
+               return snd_pcm_ioctl_channel_info_compat(substream, argp);
+       case SNDRV_PCM_IOCTL_WRITEI_FRAMES32:
+               return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
+       case SNDRV_PCM_IOCTL_READI_FRAMES32:
+               return snd_pcm_ioctl_xferi_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
+       case SNDRV_PCM_IOCTL_WRITEN_FRAMES32:
+               return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_PLAYBACK, argp);
+       case SNDRV_PCM_IOCTL_READN_FRAMES32:
+               return snd_pcm_ioctl_xfern_compat(substream, SNDRV_PCM_STREAM_CAPTURE, argp);
+       case SNDRV_PCM_IOCTL_DELAY32:
+               return snd_pcm_ioctl_delay_compat(substream, argp);
+       case SNDRV_PCM_IOCTL_REWIND32:
+               return snd_pcm_ioctl_rewind_compat(substream, argp);
+       case SNDRV_PCM_IOCTL_FORWARD32:
+               return snd_pcm_ioctl_forward_compat(substream, argp);
+       }
+
+       return -ENOIOCTLCMD;
+}
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
new file mode 100644 (file)
index 0000000..f5e6018
--- /dev/null
@@ -0,0 +1,580 @@
+/*
+ *  Routines for control of the AK4114 via I2C and 4-wire serial interface
+ *  IEC958 (S/PDIF) receiver by Asahi Kasei
+ *  Copyright (c) by Jaroslav Kysela <perex@suse.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.
+ *
+ *   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/slab.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/ak4114.h>
+#include <sound/asoundef.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("AK4114 IEC958 (S/PDIF) receiver by Asahi Kasei");
+MODULE_LICENSE("GPL");
+
+#define AK4114_ADDR                    0x00 /* fixed address */
+
+static void ak4114_stats(void *);
+
+static void reg_write(ak4114_t *ak4114, unsigned char reg, unsigned char val)
+{
+       ak4114->write(ak4114->private_data, reg, val);
+       if (reg <= AK4114_REG_INT1_MASK)
+               ak4114->regmap[reg] = val;
+       else if (reg >= AK4114_REG_RXCSB0 && reg <= AK4114_REG_TXCSB4)
+               ak4114->txcsb[reg-AK4114_REG_RXCSB0] = val;
+}
+
+static inline unsigned char reg_read(ak4114_t *ak4114, unsigned char reg)
+{
+       return ak4114->read(ak4114->private_data, reg);
+}
+
+#if 0
+static void reg_dump(ak4114_t *ak4114)
+{
+       int i;
+
+       printk("AK4114 REG DUMP:\n");
+       for (i = 0; i < 0x20; i++)
+               printk("reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < sizeof(ak4114->regmap) ? ak4114->regmap[i] : 0);
+}
+#endif
+
+static void snd_ak4114_free(ak4114_t *chip)
+{
+       chip->init = 1; /* don't schedule new work */
+       mb();
+       if (chip->workqueue != NULL) {
+               flush_workqueue(chip->workqueue);
+               destroy_workqueue(chip->workqueue);
+       }
+       kfree(chip);
+}
+
+static int snd_ak4114_dev_free(snd_device_t *device)
+{
+       ak4114_t *chip = device->device_data;
+       snd_ak4114_free(chip);
+       return 0;
+}
+
+int snd_ak4114_create(snd_card_t *card,
+                     ak4114_read_t *read, ak4114_write_t *write,
+                     unsigned char pgm[7], unsigned char txcsb[5],
+                     void *private_data, ak4114_t **r_ak4114)
+{
+       ak4114_t *chip;
+       int err = 0;
+       unsigned char reg;
+       static snd_device_ops_t ops = {
+               .dev_free =     snd_ak4114_dev_free,
+       };
+
+       chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+       if (chip == NULL)
+               return -ENOMEM;
+       spin_lock_init(&chip->lock);
+       chip->card = card;
+       chip->read = read;
+       chip->write = write;
+       chip->private_data = private_data;
+
+       for (reg = 0; reg < 7; reg++)
+               chip->regmap[reg] = pgm[reg];
+       for (reg = 0; reg < 5; reg++)
+               chip->txcsb[reg] = txcsb[reg];
+
+       chip->workqueue = create_workqueue("snd-ak4114");
+       if (chip->workqueue == NULL) {
+               kfree(chip);
+               return -ENOMEM;
+       }
+
+       snd_ak4114_reinit(chip);
+
+       chip->rcs0 = reg_read(chip, AK4114_REG_RCS0) & ~(AK4114_QINT | AK4114_CINT);
+       chip->rcs1 = reg_read(chip, AK4114_REG_RCS1);
+
+       if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0)
+               goto __fail;
+
+       if (r_ak4114)
+               *r_ak4114 = chip;
+       return 0;
+
+      __fail:
+       snd_ak4114_free(chip);
+       return err < 0 ? err : -EIO;
+}
+
+void snd_ak4114_reg_write(ak4114_t *chip, unsigned char reg, unsigned char mask, unsigned char val)
+{
+       if (reg <= AK4114_REG_INT1_MASK)
+               reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val);
+       else if (reg >= AK4114_REG_TXCSB0 && reg <= AK4114_REG_TXCSB4)
+               reg_write(chip, reg, (chip->txcsb[reg] & ~mask) | val);
+}
+
+void snd_ak4114_reinit(ak4114_t *chip)
+{
+       unsigned char old = chip->regmap[AK4114_REG_PWRDN], reg;
+
+       chip->init = 1;
+       mb();
+       flush_workqueue(chip->workqueue);
+       /* bring the chip to reset state and powerdown state */
+       reg_write(chip, AK4114_REG_PWRDN, old & ~(AK4114_RST|AK4114_PWN));
+       udelay(200);
+       /* release reset, but leave powerdown */
+       reg_write(chip, AK4114_REG_PWRDN, (old | AK4114_RST) & ~AK4114_PWN);
+       udelay(200);
+       for (reg = 1; reg < 7; reg++)
+               reg_write(chip, reg, chip->regmap[reg]);
+       for (reg = 0; reg < 5; reg++)
+               reg_write(chip, reg + AK4114_REG_TXCSB0, chip->txcsb[reg]);
+       /* release powerdown, everything is initialized now */
+       reg_write(chip, AK4114_REG_PWRDN, old | AK4114_RST | AK4114_PWN);
+       /* bring up statistics / event queing */
+       chip->init = 0;
+       INIT_WORK(&chip->work, ak4114_stats, chip);
+       queue_delayed_work(chip->workqueue, &chip->work, HZ / 10);
+}
+
+static unsigned int external_rate(unsigned char rcs1)
+{
+       switch (rcs1 & (AK4114_FS0|AK4114_FS1|AK4114_FS2|AK4114_FS3)) {
+       case AK4114_FS_32000HZ: return 32000;
+       case AK4114_FS_44100HZ: return 44100;
+       case AK4114_FS_48000HZ: return 48000;
+       case AK4114_FS_88200HZ: return 88200;
+       case AK4114_FS_96000HZ: return 96000;
+       case AK4114_FS_176400HZ: return 176400;
+       case AK4114_FS_192000HZ: return 192000;
+       default:                return 0;
+       }
+}
+
+static int snd_ak4114_in_error_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;
+       uinfo->value.integer.max = LONG_MAX;
+       return 0;
+}
+
+static int snd_ak4114_in_error_get(snd_kcontrol_t *kcontrol,
+                                  snd_ctl_elem_value_t *ucontrol)
+{
+       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       long *ptr;
+
+       spin_lock_irq(&chip->lock);
+       ptr = (long *)(((char *)chip) + kcontrol->private_value);
+       ucontrol->value.integer.value[0] = *ptr;
+       *ptr = 0;
+       spin_unlock_irq(&chip->lock);
+       return 0;
+}
+
+static int snd_ak4114_in_bit_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_ak4114_in_bit_get(snd_kcontrol_t *kcontrol,
+                                snd_ctl_elem_value_t *ucontrol)
+{
+       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       unsigned char reg = kcontrol->private_value & 0xff;
+       unsigned char bit = (kcontrol->private_value >> 8) & 0xff;
+       unsigned char inv = (kcontrol->private_value >> 31) & 1;
+
+       ucontrol->value.integer.value[0] = ((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv;
+       return 0;
+}
+
+static int snd_ak4114_rate_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;
+       uinfo->value.integer.max = 192000;
+       return 0;
+}
+
+static int snd_ak4114_rate_get(snd_kcontrol_t *kcontrol,
+                              snd_ctl_elem_value_t *ucontrol)
+{
+       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = external_rate(reg_read(chip, AK4114_REG_RCS1));
+       return 0;
+}
+
+static int snd_ak4114_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_ak4114_spdif_get(snd_kcontrol_t * kcontrol,
+                               snd_ctl_elem_value_t * ucontrol)
+{
+       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       unsigned i;
+
+       for (i = 0; i < AK4114_REG_RXCSB_SIZE; i++)
+               ucontrol->value.iec958.status[i] = reg_read(chip, AK4114_REG_RXCSB0 + i);
+       return 0;
+}
+
+static int snd_ak4114_spdif_playback_get(snd_kcontrol_t * kcontrol,
+                                        snd_ctl_elem_value_t * ucontrol)
+{
+       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       unsigned i;
+
+       for (i = 0; i < AK4114_REG_TXCSB_SIZE; i++)
+               ucontrol->value.iec958.status[i] = chip->txcsb[i];
+       return 0;
+}
+
+static int snd_ak4114_spdif_playback_put(snd_kcontrol_t * kcontrol,
+                                        snd_ctl_elem_value_t * ucontrol)
+{
+       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       unsigned i;
+
+       for (i = 0; i < AK4114_REG_TXCSB_SIZE; i++)
+               reg_write(chip, AK4114_REG_TXCSB0 + i, ucontrol->value.iec958.status[i]);
+       return 0;
+}
+
+static int snd_ak4114_spdif_mask_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_ak4114_spdif_mask_get(snd_kcontrol_t * kcontrol,
+                                     snd_ctl_elem_value_t * ucontrol)
+{
+       memset(ucontrol->value.iec958.status, 0xff, AK4114_REG_RXCSB_SIZE);
+       return 0;
+}
+
+static int snd_ak4114_spdif_pinfo(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 0xffff;
+       uinfo->count = 4;
+       return 0;
+}
+
+static int snd_ak4114_spdif_pget(snd_kcontrol_t * kcontrol,
+                                snd_ctl_elem_value_t * ucontrol)
+{
+       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       unsigned short tmp;
+
+       ucontrol->value.integer.value[0] = 0xf8f2;
+       ucontrol->value.integer.value[1] = 0x4e1f;
+       tmp = reg_read(chip, AK4114_REG_Pc0) | (reg_read(chip, AK4114_REG_Pc1) << 8);
+       ucontrol->value.integer.value[2] = tmp;
+       tmp = reg_read(chip, AK4114_REG_Pd0) | (reg_read(chip, AK4114_REG_Pd1) << 8);
+       ucontrol->value.integer.value[3] = tmp;
+       return 0;
+}
+
+static int snd_ak4114_spdif_qinfo(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       uinfo->count = AK4114_REG_QSUB_SIZE;
+       return 0;
+}
+
+static int snd_ak4114_spdif_qget(snd_kcontrol_t * kcontrol,
+                                snd_ctl_elem_value_t * ucontrol)
+{
+       ak4114_t *chip = snd_kcontrol_chip(kcontrol);
+       unsigned i;
+
+       for (i = 0; i < AK4114_REG_QSUB_SIZE; i++)
+               ucontrol->value.bytes.data[i] = reg_read(chip, AK4114_REG_QSUB_ADDR + i);
+       return 0;
+}
+
+/* Don't forget to change AK4114_CONTROLS define!!! */
+static snd_kcontrol_new_t snd_ak4114_iec958_controls[] = {
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 Parity Errors",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4114_in_error_info,
+       .get =          snd_ak4114_in_error_get,
+       .private_value = offsetof(ak4114_t, parity_errors),
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 V-Bit Errors",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4114_in_error_info,
+       .get =          snd_ak4114_in_error_get,
+       .private_value = offsetof(ak4114_t, v_bit_errors),
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 C-CRC Errors",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4114_in_error_info,
+       .get =          snd_ak4114_in_error_get,
+       .private_value = offsetof(ak4114_t, ccrc_errors),
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 Q-CRC Errors",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4114_in_error_info,
+       .get =          snd_ak4114_in_error_get,
+       .private_value = offsetof(ak4114_t, qcrc_errors),
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 External Rate",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4114_rate_info,
+       .get =          snd_ak4114_rate_get,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ,
+       .info =         snd_ak4114_spdif_mask_info,
+       .get =          snd_ak4114_spdif_mask_get,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4114_spdif_info,
+       .get =          snd_ak4114_spdif_playback_get,
+       .put =          snd_ak4114_spdif_playback_put,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK),
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ,
+       .info =         snd_ak4114_spdif_mask_info,
+       .get =          snd_ak4114_spdif_mask_get,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4114_spdif_info,
+       .get =          snd_ak4114_spdif_get,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 Preample Capture Default",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4114_spdif_pinfo,
+       .get =          snd_ak4114_spdif_pget,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 Q-subcode Capture Default",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4114_spdif_qinfo,
+       .get =          snd_ak4114_spdif_qget,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 Audio",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4114_in_bit_info,
+       .get =          snd_ak4114_in_bit_get,
+       .private_value = (1<<31) | (1<<8) | AK4114_REG_RCS0,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 Non-PCM Bitstream",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4114_in_bit_info,
+       .get =          snd_ak4114_in_bit_get,
+       .private_value = (6<<8) | AK4114_REG_RCS1,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 DTS Bitstream",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4114_in_bit_info,
+       .get =          snd_ak4114_in_bit_get,
+       .private_value = (3<<8) | AK4114_REG_RCS1,
+}
+};
+
+int snd_ak4114_build(ak4114_t *ak4114,
+                    snd_pcm_substream_t *ply_substream,
+                    snd_pcm_substream_t *cap_substream)
+{
+       snd_kcontrol_t *kctl;
+       unsigned int idx;
+       int err;
+
+       snd_assert(cap_substream, return -EINVAL);
+       ak4114->playback_substream = ply_substream;
+       ak4114->capture_substream = cap_substream;
+       for (idx = 0; idx < AK4114_CONTROLS; idx++) {
+               kctl = snd_ctl_new1(&snd_ak4114_iec958_controls[idx], ak4114);
+               if (kctl == NULL)
+                       return -ENOMEM;
+               if (!strstr(kctl->id.name, "Playback")) {
+                       if (ply_substream == NULL) {
+                               snd_ctl_free_one(kctl);
+                               ak4114->kctls[idx] = NULL;
+                               continue;
+                       }
+                       kctl->id.device = ply_substream->pcm->device;
+                       kctl->id.subdevice = ply_substream->number;
+               } else {
+                       kctl->id.device = cap_substream->pcm->device;
+                       kctl->id.subdevice = cap_substream->number;
+               }
+               err = snd_ctl_add(ak4114->card, kctl);
+               if (err < 0)
+                       return err;
+               ak4114->kctls[idx] = kctl;
+       }
+       return 0;
+}
+
+int snd_ak4114_external_rate(ak4114_t *ak4114)
+{
+       unsigned char rcs1;
+
+       rcs1 = reg_read(ak4114, AK4114_REG_RCS1);
+       return external_rate(rcs1);
+}
+
+int snd_ak4114_check_rate_and_errors(ak4114_t *ak4114, unsigned int flags)
+{
+       snd_pcm_runtime_t *runtime = ak4114->capture_substream ? ak4114->capture_substream->runtime : NULL;
+       unsigned long _flags;
+       int res = 0;
+       unsigned char rcs0, rcs1;
+       unsigned char c0, c1;
+
+       rcs1 = reg_read(ak4114, AK4114_REG_RCS1);
+       if (flags & AK4114_CHECK_NO_STAT)
+               goto __rate;
+       rcs0 = reg_read(ak4114, AK4114_REG_RCS0);
+       spin_lock_irqsave(&ak4114->lock, _flags);
+       if (rcs0 & AK4114_PAR)
+               ak4114->parity_errors++;
+       if (rcs1 & AK4114_V)
+               ak4114->v_bit_errors++;
+       if (rcs1 & AK4114_CCRC)
+               ak4114->ccrc_errors++;
+       if (rcs1 & AK4114_QCRC)
+               ak4114->qcrc_errors++;
+       c0 = (ak4114->rcs0 & (AK4114_QINT | AK4114_CINT | AK4114_PEM | AK4114_AUDION | AK4114_AUTO | AK4114_UNLCK)) ^
+                     (rcs0 & (AK4114_QINT | AK4114_CINT | AK4114_PEM | AK4114_AUDION | AK4114_AUTO | AK4114_UNLCK));
+       c1 = (ak4114->rcs1 & 0xf0) ^ (rcs1 & 0xf0);
+       ak4114->rcs0 = rcs0 & ~(AK4114_QINT | AK4114_CINT);
+       ak4114->rcs1 = rcs1;
+       spin_unlock_irqrestore(&ak4114->lock, _flags);
+
+       if (rcs0 & AK4114_PAR)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[0]->id);
+       if (rcs0 & AK4114_V)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[1]->id);
+       if (rcs1 & AK4114_CCRC)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[2]->id);
+       if (rcs1 & AK4114_QCRC)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[3]->id);
+
+       /* rate change */
+       if (c1 & 0xf0)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[4]->id);
+
+       if ((c0 & AK4114_PEM) | (c0 & AK4114_CINT))
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[9]->id);
+       if (c0 & AK4114_QINT)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[10]->id);
+
+       if (c0 & AK4114_AUDION)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[11]->id);
+       if (c0 & AK4114_AUTO)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[12]->id);
+       if (c0 & AK4114_DTSCD)
+               snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[13]->id);
+
+       if (ak4114->change_callback && (c0 | c1) != 0)
+               ak4114->change_callback(ak4114, c0, c1);
+
+      __rate:
+       /* compare rate */
+       res = external_rate(rcs1);
+       if (!(flags & AK4114_CHECK_NO_RATE) && runtime && runtime->rate != res) {
+               snd_pcm_stream_lock_irqsave(ak4114->capture_substream, _flags);
+               if (snd_pcm_running(ak4114->capture_substream)) {
+                       // printk("rate changed (%i <- %i)\n", runtime->rate, res);
+                       snd_pcm_stop(ak4114->capture_substream, SNDRV_PCM_STATE_DRAINING);
+                       wake_up(&runtime->sleep);
+                       res = 1;
+               }
+               snd_pcm_stream_unlock_irqrestore(ak4114->capture_substream, _flags);
+       }
+       return res;
+}
+
+static void ak4114_stats(void *data)
+{
+       ak4114_t *chip = (ak4114_t *)data;
+
+       if (chip->init)
+               return;
+       snd_ak4114_check_rate_and_errors(chip, 0);
+       queue_delayed_work(chip->workqueue, &chip->work, HZ / 10);
+}
+
+EXPORT_SYMBOL(snd_ak4114_create);
+EXPORT_SYMBOL(snd_ak4114_reg_write);
+EXPORT_SYMBOL(snd_ak4114_reinit);
+EXPORT_SYMBOL(snd_ak4114_build);
+EXPORT_SYMBOL(snd_ak4114_external_rate);
+EXPORT_SYMBOL(snd_ak4114_check_rate_and_errors);
diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
new file mode 100644 (file)
index 0000000..d03cb2f
--- /dev/null
@@ -0,0 +1,736 @@
+/*
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver p16v chips
+ *  Version: 0.22
+ *
+ *  FEATURES currently supported:
+ *    Output fixed at S32_LE, 2 channel to hw:0,0
+ *    Rates: 44.1, 48, 96, 192.
+ *
+ *  Changelog:
+ *  0.8
+ *    Use separate card based buffer for periods table.
+ *  0.9
+ *    Use 2 channel output streams instead of 8 channel.
+ *       (8 channel output streams might be good for ASIO type output)
+ *    Corrected speaker output, so Front -> Front etc.
+ *  0.10
+ *    Fixed missed interrupts.
+ *  0.11
+ *    Add Sound card model number and names.
+ *    Add Analog volume controls.
+ *  0.12
+ *    Corrected playback interrupts. Now interrupt per period, instead of half period.
+ *  0.13
+ *    Use single trigger for multichannel.
+ *  0.14
+ *    Mic capture now works at fixed: S32_LE, 96000Hz, Stereo.
+ *  0.15
+ *    Force buffer_size / period_size == INTEGER.
+ *  0.16
+ *    Update p16v.c to work with changed alsa api.
+ *  0.17
+ *    Update p16v.c to work with changed alsa api. Removed boot_devs.
+ *  0.18
+ *    Merging with snd-emu10k1 driver.
+ *  0.19
+ *    One stereo channel at 24bit now works.
+ *  0.20
+ *    Added better register defines.
+ *  0.21
+ *    Integrated with snd-emu10k1 driver.
+ *  0.22
+ *    Removed #if 0 ... #endif
+ *
+ *
+ *  BUGS:
+ *    Some stability problems when unloading the snd-p16v kernel module.
+ *    --
+ *
+ *  TODO:
+ *    SPDIF out.
+ *    Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz.
+ *    Currently capture fixed at 48000Hz.
+ *
+ *    --
+ *  GENERAL INFO:
+ *    Model: SB0240
+ *    P16V Chip: CA0151-DBS
+ *    Audigy 2 Chip: CA0102-IAT
+ *    AC97 Codec: STAC 9721
+ *    ADC: Philips 1361T (Stereo 24bit)
+ *    DAC: CS4382-K (8-channel, 24bit, 192Khz)
+ *
+ *  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/emu10k1.h>
+#include "p16v.h"
+
+#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
+
+/* Card IDs:
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:2002 -> Audigy2 ZS 7.1 Model:SB0350
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:1007 -> Audigy2 6.1    Model:SB0240
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:1002 -> Audigy2 Platinum  Model:SB msb0240230009266
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:2007 -> Audigy4 Pro Model:SB0380 M1SB0380472001901E
+ *
+ */
+
+ /* hardware definition */
+static snd_pcm_hardware_t snd_p16v_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_S32_LE, /* Only supports 24-bit samples padded to 32 bits. */
+       .rates =                SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 ,
+       .rate_min =             48000,
+       .rate_max =             192000,
+       .channels_min =         8, 
+       .channels_max =         8,
+       .buffer_bytes_max =     (32*1024),
+       .period_bytes_min =     64,
+       .period_bytes_max =     (16*1024),
+       .periods_min =          2,
+       .periods_max =          8,
+       .fifo_size =            0,
+};
+
+static void snd_p16v_pcm_free_substream(snd_pcm_runtime_t *runtime)
+{
+       snd_pcm_t *epcm = runtime->private_data;
+  
+       if (epcm) {
+               //snd_printk("epcm free: %p\n", epcm);
+               kfree(epcm);
+       }
+}
+
+/* open_playback callback */
+static int snd_p16v_pcm_open_playback_channel(snd_pcm_substream_t *substream, int channel_id)
+{
+       emu10k1_t *emu = snd_pcm_substream_chip(substream);
+        emu10k1_voice_t *channel = &(emu->p16v_voices[channel_id]);
+       emu10k1_pcm_t *epcm;
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       int err;
+
+       epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+        //snd_printk("epcm kcalloc: %p\n", epcm);
+
+       if (epcm == NULL)
+               return -ENOMEM;
+       epcm->emu = emu;
+       epcm->substream = substream;
+        //snd_printk("epcm device=%d, channel_id=%d\n", substream->pcm->device, channel_id);
+  
+       runtime->private_data = epcm;
+       runtime->private_free = snd_p16v_pcm_free_substream;
+  
+       runtime->hw = snd_p16v_playback_hw;
+
+        channel->emu = emu;
+        channel->number = channel_id;
+
+        channel->use=1;
+       //snd_printk("p16v: open channel_id=%d, channel=%p, use=0x%x\n", channel_id, channel, channel->use);
+        //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+        //channel->interrupt = snd_p16v_pcm_channel_interrupt;
+        channel->epcm=epcm;
+       if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+                return err;
+
+       return 0;
+}
+
+/* close callback */
+static int snd_p16v_pcm_close_playback(snd_pcm_substream_t *substream)
+{
+       emu10k1_t *emu = snd_pcm_substream_chip(substream);
+       //snd_pcm_runtime_t *runtime = substream->runtime;
+        //emu10k1_pcm_t *epcm = runtime->private_data;
+        emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use=0;
+/* FIXME: maybe zero others */
+       return 0;
+}
+
+static int snd_p16v_pcm_open_playback_front(snd_pcm_substream_t *substream)
+{
+       return snd_p16v_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);
+}
+
+/* hw_params callback */
+static int snd_p16v_pcm_hw_params_playback(snd_pcm_substream_t *substream,
+                                     snd_pcm_hw_params_t * hw_params)
+{
+       int result;
+        //snd_printk("hw_params alloc: substream=%p\n", substream);
+       result = snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+        //snd_printk("hw_params alloc: result=%d\n", result);
+       //dump_stack();
+       return result;
+}
+
+/* hw_free callback */
+static int snd_p16v_pcm_hw_free_playback(snd_pcm_substream_t *substream)
+{
+       int result;
+        //snd_printk("hw_params free: substream=%p\n", substream);
+       result = snd_pcm_lib_free_pages(substream);
+        //snd_printk("hw_params free: result=%d\n", result);
+       //dump_stack();
+       return result;
+}
+
+/* prepare playback callback */
+static int snd_p16v_pcm_prepare_playback(snd_pcm_substream_t *substream)
+{
+       emu10k1_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       //emu10k1_pcm_t *epcm = runtime->private_data;
+       int channel = substream->pcm->device - emu->p16v_device_offset;
+       u32 *table_base = (u32 *)(emu->p16v_buffer.area+(8*16*channel));
+       u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
+       int i;
+       u32 tmp;
+       
+        //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->p16v_buffer.addr, emu->p16v_buffer.area, emu->p16v_buffer.bytes);
+       tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
+        switch (runtime->rate) {
+       case 44100:
+         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x8000); /* FIXME: This will change the capture rate as well! */
+         break;
+       case 48000:
+         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x0000); /* FIXME: This will change the capture rate as well! */
+         break;
+       case 96000:
+         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x4000); /* FIXME: This will change the capture rate as well! */
+         break;
+       case 192000:
+         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x2000); /* FIXME: This will change the capture rate as well! */
+         break;
+       default:
+         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, 0x0000); /* FIXME: This will change the capture rate as well! */
+         break;
+       }
+       /* 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_emu10k1_ptr20_write(emu, PLAYBACK_LIST_ADDR, channel, emu->p16v_buffer.addr+(8*16*channel));
+       snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
+       snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_PTR, channel, 0);
+       snd_emu10k1_ptr20_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
+       snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
+       snd_emu10k1_ptr20_write(emu, PLAYBACK_POINTER, channel, 0);
+       snd_emu10k1_ptr20_write(emu, 0x07, channel, 0x0);
+       snd_emu10k1_ptr20_write(emu, 0x08, channel, 0);
+
+       return 0;
+}
+
+static void snd_p16v_intr_enable(emu10k1_t *emu, unsigned int intrenb)
+{
+       unsigned long flags;
+       unsigned int enable;
+
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       enable = inl(emu->port + INTE2) | intrenb;
+       outl(enable, emu->port + INTE2);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_p16v_intr_disable(emu10k1_t *emu, unsigned int intrenb)
+{
+       unsigned long flags;
+       unsigned int disable;
+
+       spin_lock_irqsave(&emu->emu_lock, flags);
+       disable = inl(emu->port + INTE2) & (~intrenb);
+       outl(disable, emu->port + INTE2);
+       spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+/* trigger_playback callback */
+static int snd_p16v_pcm_trigger_playback(snd_pcm_substream_t *substream,
+                                   int cmd)
+{
+       emu10k1_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime;
+       emu10k1_pcm_t *epcm;
+       int channel;
+       int result = 0;
+       struct list_head *pos;
+        snd_pcm_substream_t *s;
+       u32 basic = 0;
+       u32 inte = 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 = substream->pcm->device-emu->p16v_device_offset;
+               //snd_printk("p16v channel=%d\n",channel);
+               epcm->running = running;
+               basic |= (0x1<<channel);
+               inte |= (INTE2_PLAYBACK_CH_0_LOOP<<channel);
+                snd_pcm_trigger_done(s, substream);
+        }
+       //snd_printk("basic=0x%x, inte=0x%x\n",basic, inte);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               snd_p16v_intr_enable(emu, inte);
+               snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)| (basic));
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(basic));
+               snd_p16v_intr_disable(emu, inte);
+               break;
+       default:
+               result = -EINVAL;
+               break;
+       }
+       return result;
+}
+
+/* pointer_playback callback */
+static snd_pcm_uframes_t
+snd_p16v_pcm_pointer_playback(snd_pcm_substream_t *substream)
+{
+       emu10k1_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       emu10k1_pcm_t *epcm = runtime->private_data;
+       snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
+       int channel = substream->pcm->device - emu->p16v_device_offset;
+       if (!epcm->running)
+               return 0;
+
+       ptr3 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
+       ptr1 = snd_emu10k1_ptr20_read(emu, PLAYBACK_POINTER, channel);
+       ptr4 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
+       if (ptr3 != ptr4) ptr1 = snd_emu10k1_ptr20_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_p16v_playback_front_ops = {
+       .open =        snd_p16v_pcm_open_playback_front,
+       .close =       snd_p16v_pcm_close_playback,
+       .ioctl =       snd_pcm_lib_ioctl,
+       .hw_params =   snd_p16v_pcm_hw_params_playback,
+       .hw_free =     snd_p16v_pcm_hw_free_playback,
+       .prepare =     snd_p16v_pcm_prepare_playback,
+       .trigger =     snd_p16v_pcm_trigger_playback,
+       .pointer =     snd_p16v_pcm_pointer_playback,
+};
+
+int snd_p16v_free(emu10k1_t *chip)
+{
+       // release the data
+       if (chip->p16v_buffer.area) {
+               snd_dma_free_pages(&chip->p16v_buffer);
+               //snd_printk("period lables free: %p\n", &chip->p16v_buffer);
+       }
+       return 0;
+}
+
+static void snd_p16v_pcm_free(snd_pcm_t *pcm)
+{
+       emu10k1_t *emu = pcm->private_data;
+       //snd_printk("snd_p16v_pcm_free pcm: called\n");
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+       emu->pcm = NULL;
+}
+
+int snd_p16v_pcm(emu10k1_t *emu, int device, snd_pcm_t **rpcm)
+{
+       snd_pcm_t *pcm;
+       snd_pcm_substream_t *substream;
+       int err;
+        int capture=0;
+  
+       //snd_printk("snd_p16v_pcm called. device=%d\n", device);
+       emu->p16v_device_offset = device;
+       if (rpcm)
+               *rpcm = NULL;
+        //if (device == 0) capture=1; 
+       if ((err = snd_pcm_new(emu->card, "p16v", device, 1, capture, &pcm)) < 0)
+               return err;
+  
+       pcm->private_data = emu;
+       pcm->private_free = snd_p16v_pcm_free;
+
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_p16v_playback_front_ops);
+
+       pcm->info_flags = 0;
+       pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+       strcpy(pcm->name, "p16v");
+       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;
+               //snd_printk("preallocate playback substream: err=%d\n", 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;
+               //snd_printk("preallocate capture substream: err=%d\n", err);
+       }
+  
+       if (rpcm)
+               *rpcm = pcm;
+  
+       return 0;
+}
+
+static int snd_p16v_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_p16v_volume_get(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol, int reg, int high_low)
+{
+        emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+        u32 value;
+
+        value = snd_emu10k1_ptr20_read(emu, reg, high_low);
+       if (high_low == 1) {
+               ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
+               ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
+       } else {
+               ucontrol->value.integer.value[0] = 0xff - ((value >> 8) & 0xff); /* Left */
+               ucontrol->value.integer.value[1] = 0xff - ((value >> 0) & 0xff); /* Right */
+       }
+        return 0;
+}
+
+static int snd_p16v_volume_get_spdif_front(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 0;
+       int reg = PLAYBACK_VOLUME_MIXER7;
+        return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_get_spdif_center_lfe(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 1;
+       int reg = PLAYBACK_VOLUME_MIXER7;
+        return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+static int snd_p16v_volume_get_spdif_unknown(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 0;
+       int reg = PLAYBACK_VOLUME_MIXER8;
+        return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+static int snd_p16v_volume_get_spdif_rear(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 1;
+       int reg = PLAYBACK_VOLUME_MIXER8;
+        return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_get_analog_front(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 0;
+       int reg = PLAYBACK_VOLUME_MIXER9;
+        return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_get_analog_center_lfe(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 1;
+       int reg = PLAYBACK_VOLUME_MIXER9;
+        return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+static int snd_p16v_volume_get_analog_rear(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 1;
+       int reg = PLAYBACK_VOLUME_MIXER10;
+        return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_get_analog_unknown(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 0;
+       int reg = PLAYBACK_VOLUME_MIXER10;
+        return snd_p16v_volume_get(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol, int reg, int high_low)
+{
+        emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+        u32 value;
+        value = snd_emu10k1_ptr20_read(emu, reg, 0);
+        //value = value & 0xffff;
+       if (high_low == 1) {
+               value &= 0xffff;
+               value = value | ((0xff - ucontrol->value.integer.value[0]) << 24) | ((0xff - ucontrol->value.integer.value[1]) << 16);
+       } else {
+               value &= 0xffff0000;
+               value = value | ((0xff - ucontrol->value.integer.value[0]) << 8) | ((0xff - ucontrol->value.integer.value[1]) );
+       }
+               snd_emu10k1_ptr20_write(emu, reg, 0, value);
+        return 1;
+}
+
+static int snd_p16v_volume_put_spdif_front(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 0;
+       int reg = PLAYBACK_VOLUME_MIXER7;
+        return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put_spdif_center_lfe(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 1;
+       int reg = PLAYBACK_VOLUME_MIXER7;
+        return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put_spdif_unknown(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 0;
+       int reg = PLAYBACK_VOLUME_MIXER8;
+        return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put_spdif_rear(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 1;
+       int reg = PLAYBACK_VOLUME_MIXER8;
+        return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put_analog_front(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 0;
+       int reg = PLAYBACK_VOLUME_MIXER9;
+        return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put_analog_center_lfe(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 1;
+       int reg = PLAYBACK_VOLUME_MIXER9;
+        return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put_analog_rear(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 1;
+       int reg = PLAYBACK_VOLUME_MIXER10;
+        return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static int snd_p16v_volume_put_analog_unknown(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       int high_low = 0;
+       int reg = PLAYBACK_VOLUME_MIXER10;
+        return snd_p16v_volume_put(kcontrol, ucontrol, reg, high_low);
+}
+
+static snd_kcontrol_new_t snd_p16v_volume_control_analog_front =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "HD Analog Front Volume",
+        .info =         snd_p16v_volume_info,
+        .get =          snd_p16v_volume_get_analog_front,
+        .put =          snd_p16v_volume_put_analog_front
+};
+
+static snd_kcontrol_new_t snd_p16v_volume_control_analog_center_lfe =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "HD Analog Center/LFE Volume",
+        .info =         snd_p16v_volume_info,
+        .get =          snd_p16v_volume_get_analog_center_lfe,
+        .put =          snd_p16v_volume_put_analog_center_lfe
+};
+
+static snd_kcontrol_new_t snd_p16v_volume_control_analog_unknown =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "HD Analog Unknown Volume",
+        .info =         snd_p16v_volume_info,
+        .get =          snd_p16v_volume_get_analog_unknown,
+        .put =          snd_p16v_volume_put_analog_unknown
+};
+
+static snd_kcontrol_new_t snd_p16v_volume_control_analog_rear =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "HD Analog Rear Volume",
+        .info =         snd_p16v_volume_info,
+        .get =          snd_p16v_volume_get_analog_rear,
+        .put =          snd_p16v_volume_put_analog_rear
+};
+
+static snd_kcontrol_new_t snd_p16v_volume_control_spdif_front =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "HD SPDIF Front Volume",
+        .info =         snd_p16v_volume_info,
+        .get =          snd_p16v_volume_get_spdif_front,
+        .put =          snd_p16v_volume_put_spdif_front
+};
+
+static snd_kcontrol_new_t snd_p16v_volume_control_spdif_center_lfe =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "HD SPDIF Center/LFE Volume",
+        .info =         snd_p16v_volume_info,
+        .get =          snd_p16v_volume_get_spdif_center_lfe,
+        .put =          snd_p16v_volume_put_spdif_center_lfe
+};
+
+static snd_kcontrol_new_t snd_p16v_volume_control_spdif_unknown =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "HD SPDIF Unknown Volume",
+        .info =         snd_p16v_volume_info,
+        .get =          snd_p16v_volume_get_spdif_unknown,
+        .put =          snd_p16v_volume_put_spdif_unknown
+};
+
+static snd_kcontrol_new_t snd_p16v_volume_control_spdif_rear =
+{
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "HD SPDIF Rear Volume",
+        .info =         snd_p16v_volume_info,
+        .get =          snd_p16v_volume_get_spdif_rear,
+        .put =          snd_p16v_volume_put_spdif_rear
+};
+
+int snd_p16v_mixer(emu10k1_t *emu)
+{
+        int err;
+        snd_kcontrol_t *kctl;
+        snd_card_t *card = emu->card;
+        if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_analog_front, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_analog_rear, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_analog_center_lfe, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_analog_unknown, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_spdif_front, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_spdif_rear, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_spdif_center_lfe, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_p16v_volume_control_spdif_unknown, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        return 0;
+}
+
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
new file mode 100644 (file)
index 0000000..570a59d
--- /dev/null
@@ -0,0 +1,7 @@
+snd-hda-intel-objs := hda_intel.o
+snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o
+ifdef CONFIG_PROC_FS
+snd-hda-codec-objs += hda_proc.o
+endif
+
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o snd-hda-codec.o
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
new file mode 100644 (file)
index 0000000..9ed117a
--- /dev/null
@@ -0,0 +1,1856 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the 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 driver 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/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include "hda_local.h"
+
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("Universal interface for High Definition Audio Codec");
+MODULE_LICENSE("GPL");
+
+
+/*
+ * vendor / preset table
+ */
+
+struct hda_vendor_id {
+       unsigned int id;
+       const char *name;
+};
+
+/* codec vendor labels */
+static struct hda_vendor_id hda_vendor_ids[] = {
+       { 0x10ec, "Realtek" },
+       { 0x13f6, "C-Media" },
+       { 0x434d, "C-Media" },
+       {} /* terminator */
+};
+
+/* codec presets */
+#include "hda_patch.h"
+
+
+/**
+ * snd_hda_codec_read - send a command and get the response
+ * @codec: the HDA codec
+ * @nid: NID to send the command
+ * @direct: direct flag
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command and read the corresponding response.
+ *
+ * Returns the obtained response value, or -1 for an error.
+ */
+unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int direct,
+                               unsigned int verb, unsigned int parm)
+{
+       unsigned int res;
+       down(&codec->bus->cmd_mutex);
+       if (! codec->bus->ops.command(codec, nid, direct, verb, parm))
+               res = codec->bus->ops.get_response(codec);
+       else
+               res = (unsigned int)-1;
+       up(&codec->bus->cmd_mutex);
+       return res;
+}
+
+/**
+ * snd_hda_codec_write - send a single command without waiting for response
+ * @codec: the HDA codec
+ * @nid: NID to send the command
+ * @direct: direct flag
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command without waiting for response.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
+                        unsigned int verb, unsigned int parm)
+{
+       int err;
+       down(&codec->bus->cmd_mutex);
+       err = codec->bus->ops.command(codec, nid, direct, verb, parm);
+       up(&codec->bus->cmd_mutex);
+       return err;
+}
+
+/**
+ * snd_hda_sequence_write - sequence writes
+ * @codec: the HDA codec
+ * @seq: VERB array to send
+ *
+ * Send the commands sequentially from the given array.
+ * The array must be terminated with NID=0.
+ */
+void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq)
+{
+       for (; seq->nid; seq++)
+               snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
+}
+
+/**
+ * snd_hda_get_sub_nodes - get the range of sub nodes
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @start_id: the pointer to store the start NID
+ *
+ * Parse the NID and store the start NID of its sub-nodes.
+ * Returns the number of sub-nodes.
+ */
+int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id)
+{
+       unsigned int parm;
+
+       parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT);
+       *start_id = (parm >> 16) & 0x7fff;
+       return (int)(parm & 0x7fff);
+}
+
+/**
+ * snd_hda_get_connections - get connection list
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @conn_list: connection list array
+ * @max_conns: max. number of connections to store
+ *
+ * Parses the connection list of the given widget and stores the list
+ * of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
+ */
+int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
+                           hda_nid_t *conn_list, int max_conns)
+{
+       unsigned int parm;
+       int i, j, conn_len, num_tupples, conns;
+       unsigned int shift, num_elems, mask;
+
+       snd_assert(conn_list && max_conns > 0, return -EINVAL);
+
+       parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
+       if (parm & AC_CLIST_LONG) {
+               /* long form */
+               shift = 16;
+               num_elems = 2;
+       } else {
+               /* short form */
+               shift = 8;
+               num_elems = 4;
+       }
+       conn_len = parm & AC_CLIST_LENGTH;
+       num_tupples = num_elems / 2;
+       mask = (1 << (shift-1)) - 1;
+
+       if (! conn_len)
+               return 0; /* no connection */
+
+       if (conn_len == 1) {
+               /* single connection */
+               parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_LIST, 0);
+               conn_list[0] = parm & mask;
+               return 1;
+       }
+
+       /* multi connection */
+       conns = 0;
+       for (i = 0; i < conn_len; i += num_elems) {
+               parm = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_LIST, i);
+               for (j = 0; j < num_tupples; j++) {
+                       int range_val;
+                       hda_nid_t val1, val2, n;
+                       range_val = parm & (1 << (shift-1)); /* ranges */
+                       val1 = parm & mask;
+                       parm >>= shift;
+                       val2 = parm & mask;
+                       parm >>= shift;
+                       if (range_val) {
+                               /* ranges between val1 and val2 */
+                               if (val1 > val2) {
+                                       snd_printk(KERN_WARNING "hda_codec: invalid dep_range_val %x:%x\n", val1, val2);
+                                       continue;
+                               }
+                               for (n = val1; n <= val2; n++) {
+                                       if (conns >= max_conns)
+                                               return -EINVAL;
+                                       conn_list[conns++] = n;
+                               }
+                       } else {
+                               if (! val1)
+                                       break;
+                               if (conns >= max_conns)
+                                       return -EINVAL;
+                               conn_list[conns++] = val1;
+                               if (! val2)
+                                       break;
+                               if (conns >= max_conns)
+                                       return -EINVAL;
+                               conn_list[conns++] = val2;
+                       }
+               }
+       }
+       return conns;
+}
+
+
+/**
+ * snd_hda_queue_unsol_event - add an unsolicited event to queue
+ * @bus: the BUS
+ * @res: unsolicited event (lower 32bit of RIRB entry)
+ * @res_ex: codec addr and flags (upper 32bit or RIRB entry)
+ *
+ * Adds the given event to the queue.  The events are processed in
+ * the workqueue asynchronously.  Call this function in the interrupt
+ * hanlder when RIRB receives an unsolicited event.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
+{
+       struct hda_bus_unsolicited *unsol;
+       unsigned int wp;
+
+       if ((unsol = bus->unsol) == NULL)
+               return 0;
+
+       wp = (unsol->wp + 1) % HDA_UNSOL_QUEUE_SIZE;
+       unsol->wp = wp;
+
+       wp <<= 1;
+       unsol->queue[wp] = res;
+       unsol->queue[wp + 1] = res_ex;
+
+       queue_work(unsol->workq, &unsol->work);
+
+       return 0;
+}
+
+/*
+ * process queueud unsolicited events
+ */
+static void process_unsol_events(void *data)
+{
+       struct hda_bus *bus = data;
+       struct hda_bus_unsolicited *unsol = bus->unsol;
+       struct hda_codec *codec;
+       unsigned int rp, caddr, res;
+
+       while (unsol->rp != unsol->wp) {
+               rp = (unsol->rp + 1) % HDA_UNSOL_QUEUE_SIZE;
+               unsol->rp = rp;
+               rp <<= 1;
+               res = unsol->queue[rp];
+               caddr = unsol->queue[rp + 1];
+               if (! (caddr & (1 << 4))) /* no unsolicited event? */
+                       continue;
+               codec = bus->caddr_tbl[caddr & 0x0f];
+               if (codec && codec->patch_ops.unsol_event)
+                       codec->patch_ops.unsol_event(codec, res);
+       }
+}
+
+/*
+ * initialize unsolicited queue
+ */
+static int init_unsol_queue(struct hda_bus *bus)
+{
+       struct hda_bus_unsolicited *unsol;
+
+       unsol = kcalloc(1, sizeof(*unsol), GFP_KERNEL);
+       if (! unsol) {
+               snd_printk(KERN_ERR "hda_codec: can't allocate unsolicited queue\n");
+               return -ENOMEM;
+       }
+       unsol->workq = create_workqueue("hda_codec");
+       if (! unsol->workq) {
+               snd_printk(KERN_ERR "hda_codec: can't create workqueue\n");
+               kfree(unsol);
+               return -ENOMEM;
+       }
+       INIT_WORK(&unsol->work, process_unsol_events, bus);
+       bus->unsol = unsol;
+       return 0;
+}
+
+/*
+ * destructor
+ */
+static void snd_hda_codec_free(struct hda_codec *codec);
+
+static int snd_hda_bus_free(struct hda_bus *bus)
+{
+       struct list_head *p, *n;
+
+       if (! bus)
+               return 0;
+       if (bus->unsol) {
+               destroy_workqueue(bus->unsol->workq);
+               kfree(bus->unsol);
+       }
+       list_for_each_safe(p, n, &bus->codec_list) {
+               struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+               snd_hda_codec_free(codec);
+       }
+       if (bus->ops.private_free)
+               bus->ops.private_free(bus);
+       kfree(bus);
+       return 0;
+}
+
+static int snd_hda_bus_dev_free(snd_device_t *device)
+{
+       struct hda_bus *bus = device->device_data;
+       return snd_hda_bus_free(bus);
+}
+
+/**
+ * snd_hda_bus_new - create a HDA bus
+ * @card: the card entry
+ * @temp: the template for hda_bus information
+ * @busp: the pointer to store the created bus instance
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_bus_new(snd_card_t *card, const struct hda_bus_template *temp,
+                   struct hda_bus **busp)
+{
+       struct hda_bus *bus;
+       int err;
+       static snd_device_ops_t dev_ops = {
+               .dev_free = snd_hda_bus_dev_free,
+       };
+
+       snd_assert(temp, return -EINVAL);
+       snd_assert(temp->ops.command && temp->ops.get_response, return -EINVAL);
+
+       if (busp)
+               *busp = NULL;
+
+       bus = kcalloc(1, sizeof(*bus), GFP_KERNEL);
+       if (bus == NULL) {
+               snd_printk(KERN_ERR "can't allocate struct hda_bus\n");
+               return -ENOMEM;
+       }
+
+       bus->card = card;
+       bus->private_data = temp->private_data;
+       bus->pci = temp->pci;
+       bus->modelname = temp->modelname;
+       bus->ops = temp->ops;
+
+       init_MUTEX(&bus->cmd_mutex);
+       INIT_LIST_HEAD(&bus->codec_list);
+
+       init_unsol_queue(bus);
+
+       if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)) < 0) {
+               snd_hda_bus_free(bus);
+               return err;
+       }
+       if (busp)
+               *busp = bus;
+       return 0;
+}
+
+
+/*
+ * find a matching codec preset
+ */
+static const struct hda_codec_preset *find_codec_preset(struct hda_codec *codec)
+{
+       const struct hda_codec_preset **tbl, *preset;
+
+       for (tbl = hda_preset_tables; *tbl; tbl++) {
+               for (preset = *tbl; preset->id; preset++) {
+                       u32 mask = preset->mask;
+                       if (! mask)
+                               mask = ~0;
+                       if (preset->id == (codec->vendor_id & mask))
+                               return preset;
+               }
+       }
+       return NULL;
+}
+
+/*
+ * snd_hda_get_codec_name - store the codec name
+ */
+void snd_hda_get_codec_name(struct hda_codec *codec,
+                           char *name, int namelen)
+{
+       const struct hda_vendor_id *c;
+       const char *vendor = NULL;
+       u16 vendor_id = codec->vendor_id >> 16;
+       char tmp[16];
+
+       for (c = hda_vendor_ids; c->id; c++) {
+               if (c->id == vendor_id) {
+                       vendor = c->name;
+                       break;
+               }
+       }
+       if (! vendor) {
+               sprintf(tmp, "Generic %04x", vendor_id);
+               vendor = tmp;
+       }
+       if (codec->preset && codec->preset->name)
+               snprintf(name, namelen, "%s %s", vendor, codec->preset->name);
+       else
+               snprintf(name, namelen, "%s ID %x", vendor, codec->vendor_id & 0xffff);
+}
+
+/*
+ * look for an AFG node
+ *
+ * return 0 if not found
+ */
+static int look_for_afg_node(struct hda_codec *codec)
+{
+       int i, total_nodes;
+       hda_nid_t nid;
+
+       total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid);
+       for (i = 0; i < total_nodes; i++, nid++) {
+               if ((snd_hda_param_read(codec, nid, AC_PAR_FUNCTION_TYPE) & 0xff) ==
+                   AC_GRP_AUDIO_FUNCTION)
+                       return nid;
+       }
+       return 0;
+}
+
+/*
+ * codec destructor
+ */
+static void snd_hda_codec_free(struct hda_codec *codec)
+{
+       if (! codec)
+               return;
+       list_del(&codec->list);
+       codec->bus->caddr_tbl[codec->addr] = NULL;
+       if (codec->patch_ops.free)
+               codec->patch_ops.free(codec);
+       kfree(codec);
+}
+
+static void init_amp_hash(struct hda_codec *codec);
+
+/**
+ * snd_hda_codec_new - create a HDA codec
+ * @bus: the bus to assign
+ * @codec_addr: the codec address
+ * @codecp: the pointer to store the generated codec
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
+                     struct hda_codec **codecp)
+{
+       struct hda_codec *codec;
+       char component[13];
+       int err;
+
+       snd_assert(bus, return -EINVAL);
+       snd_assert(codec_addr <= HDA_MAX_CODEC_ADDRESS, return -EINVAL);
+
+       if (bus->caddr_tbl[codec_addr]) {
+               snd_printk(KERN_ERR "hda_codec: address 0x%x is already occupied\n", codec_addr);
+               return -EBUSY;
+       }
+
+       codec = kcalloc(1, sizeof(*codec), GFP_KERNEL);
+       if (codec == NULL) {
+               snd_printk(KERN_ERR "can't allocate struct hda_codec\n");
+               return -ENOMEM;
+       }
+
+       codec->bus = bus;
+       codec->addr = codec_addr;
+       init_MUTEX(&codec->spdif_mutex);
+       init_amp_hash(codec);
+
+       list_add_tail(&codec->list, &bus->codec_list);
+       bus->caddr_tbl[codec_addr] = codec;
+
+       codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_VENDOR_ID);
+       codec->subsystem_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_SUBSYSTEM_ID);
+       codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_REV_ID);
+
+       /* FIXME: support for multiple AFGs? */
+       codec->afg = look_for_afg_node(codec);
+       if (! codec->afg) {
+               snd_printk(KERN_ERR "hda_codec: no AFG node found\n");
+               snd_hda_codec_free(codec);
+               return -ENODEV;
+       }
+
+       codec->preset = find_codec_preset(codec);
+       if (! *bus->card->mixername)
+               snd_hda_get_codec_name(codec, bus->card->mixername,
+                                      sizeof(bus->card->mixername));
+
+       if (codec->preset && codec->preset->patch)
+               err = codec->preset->patch(codec);
+       else
+               err = snd_hda_parse_generic_codec(codec);
+       if (err < 0) {
+               snd_hda_codec_free(codec);
+               return err;
+       }
+
+       snd_hda_codec_proc_new(codec);
+
+       sprintf(component, "HDA:%08x", codec->vendor_id);
+       snd_component_add(codec->bus->card, component);
+
+       if (codecp)
+               *codecp = codec;
+       return 0;
+}
+
+/**
+ * snd_hda_codec_setup_stream - set up the codec for streaming
+ * @codec: the CODEC to set up
+ * @nid: the NID to set up
+ * @stream_tag: stream tag to pass, it's between 0x1 and 0xf.
+ * @channel_id: channel id to pass, zero based.
+ * @format: stream format.
+ */
+void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag,
+                               int channel_id, int format)
+{
+       snd_printdd("hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
+                   nid, stream_tag, channel_id, format);
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID,
+                           (stream_tag << 4) | channel_id);
+       msleep(1);
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
+}
+
+
+/*
+ * amp access functions
+ */
+
+#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + (idx) * 32 + (dir) * 64)
+#define INFO_AMP_CAPS  (1<<0)
+#define INFO_AMP_VOL   (1<<1)
+
+/* initialize the hash table */
+static void init_amp_hash(struct hda_codec *codec)
+{
+       memset(codec->amp_hash, 0xff, sizeof(codec->amp_hash));
+       codec->num_amp_entries = 0;
+}
+
+/* query the hash.  allocate an entry if not found. */
+static struct hda_amp_info *get_alloc_amp_hash(struct hda_codec *codec, u32 key)
+{
+       u16 idx = key % (u16)ARRAY_SIZE(codec->amp_hash);
+       u16 cur = codec->amp_hash[idx];
+       struct hda_amp_info *info;
+
+       while (cur != 0xffff) {
+               info = &codec->amp_info[cur];
+               if (info->key == key)
+                       return info;
+               cur = info->next;
+       }
+
+       /* add a new hash entry */
+       if (codec->num_amp_entries >= ARRAY_SIZE(codec->amp_info)) {
+               snd_printk(KERN_ERR "hda_codec: Tooooo many amps!\n");
+               return NULL;
+       }
+       cur = codec->num_amp_entries++;
+       info = &codec->amp_info[cur];
+       info->key = key;
+       info->status = 0; /* not initialized yet */
+       info->next = codec->amp_hash[idx];
+       codec->amp_hash[idx] = cur;
+
+       return info;
+}
+
+/*
+ * query AMP capabilities for the given widget and direction
+ */
+static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
+{
+       struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0));
+
+       if (! info)
+               return 0;
+       if (! (info->status & INFO_AMP_CAPS)) {
+               if (!(snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_AMP_OVRD))
+                       nid = codec->afg;
+               info->amp_caps = snd_hda_param_read(codec, nid, direction == HDA_OUTPUT ?
+                                                   AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+               info->status |= INFO_AMP_CAPS;
+       }
+       return info->amp_caps;
+}
+
+/*
+ * read the current volume to info
+ * if the cache exists, read from the cache.
+ */
+static void get_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
+                        hda_nid_t nid, int ch, int direction, int index)
+{
+       u32 val, parm;
+
+       if (info->status & (INFO_AMP_VOL << ch))
+               return;
+
+       parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
+       parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
+       parm |= index;
+       val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, parm);
+       info->vol[ch] = val & 0xff;
+       info->status |= INFO_AMP_VOL << ch;
+}
+
+/*
+ * write the current volume in info to the h/w
+ */
+static void put_vol_mute(struct hda_codec *codec,
+                        hda_nid_t nid, int ch, int direction, int index, int val)
+{
+       u32 parm;
+
+       parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;
+       parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;
+       parm |= index << AC_AMP_SET_INDEX_SHIFT;
+       parm |= val;
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
+}
+
+/*
+ * read/write AMP value.  The volume is between 0 to 0x7f, 0x80 = mute bit.
+ */
+int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index)
+{
+       struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
+       if (! info)
+               return 0;
+       get_vol_mute(codec, info, nid, ch, direction, index);
+       return info->vol[ch];
+}
+
+int snd_hda_codec_amp_write(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int val)
+{
+       struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
+       if (! info)
+               return 0;
+       get_vol_mute(codec, info, nid, ch, direction, idx);
+       if (info->vol[ch] == val && ! codec->in_resume)
+               return 0;
+       put_vol_mute(codec, nid, ch, direction, idx, val);
+       info->vol[ch] = val;
+       return 1;
+}
+
+
+/*
+ * AMP control callbacks
+ */
+/* retrieve parameters from private_value */
+#define get_amp_nid(kc)                ((kc)->private_value & 0xffff)
+#define get_amp_channels(kc)   (((kc)->private_value >> 16) & 0x3)
+#define get_amp_direction(kc)  (((kc)->private_value >> 18) & 0x1)
+#define get_amp_index(kc)      (((kc)->private_value >> 19) & 0xf)
+
+/* volume */
+int snd_hda_mixer_amp_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       u16 nid = get_amp_nid(kcontrol);
+       u8 chs = get_amp_channels(kcontrol);
+       int dir = get_amp_direction(kcontrol);
+       u32 caps;
+
+       caps = query_amp_caps(codec, nid, dir);
+       caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; /* num steps */
+       if (! caps) {
+               printk(KERN_WARNING "hda_codec: num_steps = 0 for NID=0x%x\n", nid);
+               return -EINVAL;
+       }
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = chs == 3 ? 2 : 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = caps;
+       return 0;
+}
+
+int snd_hda_mixer_amp_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = get_amp_nid(kcontrol);
+       int chs = get_amp_channels(kcontrol);
+       int dir = get_amp_direction(kcontrol);
+       int idx = get_amp_index(kcontrol);
+       long *valp = ucontrol->value.integer.value;
+
+       if (chs & 1)
+               *valp++ = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x7f;
+       if (chs & 2)
+               *valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x7f;
+       return 0;
+}
+
+int snd_hda_mixer_amp_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = get_amp_nid(kcontrol);
+       int chs = get_amp_channels(kcontrol);
+       int dir = get_amp_direction(kcontrol);
+       int idx = get_amp_index(kcontrol);
+       int val;
+       long *valp = ucontrol->value.integer.value;
+       int change = 0;
+
+       if (chs & 1) {
+               val = *valp & 0x7f;
+               val |= snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x80;
+               change = snd_hda_codec_amp_write(codec, nid, 0, dir, idx, val);
+               valp++;
+       }
+       if (chs & 2) {
+               val = *valp & 0x7f;
+               val |= snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x80;
+               change |= snd_hda_codec_amp_write(codec, nid, 1, dir, idx, val);
+       }
+       return change;
+}
+
+/* switch */
+int snd_hda_mixer_amp_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       int chs = get_amp_channels(kcontrol);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = chs == 3 ? 2 : 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+int snd_hda_mixer_amp_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = get_amp_nid(kcontrol);
+       int chs = get_amp_channels(kcontrol);
+       int dir = get_amp_direction(kcontrol);
+       int idx = get_amp_index(kcontrol);
+       long *valp = ucontrol->value.integer.value;
+
+       if (chs & 1)
+               *valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x80) ? 0 : 1;
+       if (chs & 2)
+               *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x80) ? 0 : 1;
+       return 0;
+}
+
+int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = get_amp_nid(kcontrol);
+       int chs = get_amp_channels(kcontrol);
+       int dir = get_amp_direction(kcontrol);
+       int idx = get_amp_index(kcontrol);
+       int val;
+       long *valp = ucontrol->value.integer.value;
+       int change = 0;
+
+       if (chs & 1) {
+               val = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x7f;
+               val |= *valp ? 0 : 0x80;
+               change = snd_hda_codec_amp_write(codec, nid, 0, dir, idx, val);
+               valp++;
+       }
+       if (chs & 2) {
+               val = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x7f;
+               val |= *valp ? 0 : 0x80;
+               change = snd_hda_codec_amp_write(codec, nid, 1, dir, idx, val);
+       }
+       return change;
+}
+
+/*
+ * SPDIF out controls
+ */
+
+static int snd_hda_spdif_mask_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_hda_spdif_cmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+                                          IEC958_AES0_NONAUDIO |
+                                          IEC958_AES0_CON_EMPHASIS_5015 |
+                                          IEC958_AES0_CON_NOT_COPYRIGHT;
+       ucontrol->value.iec958.status[1] = IEC958_AES1_CON_CATEGORY |
+                                          IEC958_AES1_CON_ORIGINAL;
+       return 0;
+}
+
+static int snd_hda_spdif_pmask_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ucontrol->value.iec958.status[0] = IEC958_AES0_PROFESSIONAL |
+                                          IEC958_AES0_NONAUDIO |
+                                          IEC958_AES0_PRO_EMPHASIS_5015;
+       return 0;
+}
+
+static int snd_hda_spdif_default_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.iec958.status[0] = codec->spdif_status & 0xff;
+       ucontrol->value.iec958.status[1] = (codec->spdif_status >> 8) & 0xff;
+       ucontrol->value.iec958.status[2] = (codec->spdif_status >> 16) & 0xff;
+       ucontrol->value.iec958.status[3] = (codec->spdif_status >> 24) & 0xff;
+
+       return 0;
+}
+
+/* convert from SPDIF status bits to HDA SPDIF bits
+ * bit 0 (DigEn) is always set zero (to be filled later)
+ */
+static unsigned short convert_from_spdif_status(unsigned int sbits)
+{
+       unsigned short val = 0;
+
+       if (sbits & IEC958_AES0_PROFESSIONAL)
+               val |= 1 << 6;
+       if (sbits & IEC958_AES0_NONAUDIO)
+               val |= 1 << 5;
+       if (sbits & IEC958_AES0_PROFESSIONAL) {
+               if ((sbits & IEC958_AES0_PRO_EMPHASIS) == IEC958_AES0_PRO_EMPHASIS_5015)
+                       val |= 1 << 3;
+       } else {
+               if ((sbits & IEC958_AES0_CON_EMPHASIS) == IEC958_AES0_CON_EMPHASIS_5015)
+                       val |= 1 << 3;
+               if (! (sbits & IEC958_AES0_CON_NOT_COPYRIGHT))
+                       val |= 1 << 4;
+               if (sbits & (IEC958_AES1_CON_ORIGINAL << 8))
+                       val |= 1 << 7;
+               val |= sbits & (IEC958_AES1_CON_CATEGORY << 8);
+       }
+       return val;
+}
+
+/* convert to SPDIF status bits from HDA SPDIF bits
+ */
+static unsigned int convert_to_spdif_status(unsigned short val)
+{
+       unsigned int sbits = 0;
+
+       if (val & (1 << 5))
+               sbits |= IEC958_AES0_NONAUDIO;
+       if (val & (1 << 6))
+               sbits |= IEC958_AES0_PROFESSIONAL;
+       if (sbits & IEC958_AES0_PROFESSIONAL) {
+               if (sbits & (1 << 3))
+                       sbits |= IEC958_AES0_PRO_EMPHASIS_5015;
+       } else {
+               if (val & (1 << 3))
+                       sbits |= IEC958_AES0_CON_EMPHASIS_5015;
+               if (! (val & (1 << 4)))
+                       sbits |= IEC958_AES0_CON_NOT_COPYRIGHT;
+               if (val & (1 << 7))
+                       sbits |= (IEC958_AES1_CON_ORIGINAL << 8);
+               sbits |= val & (0x7f << 8);
+       }
+       return sbits;
+}
+
+static int snd_hda_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = kcontrol->private_value;
+       unsigned short val;
+       int change;
+
+       down(&codec->spdif_mutex);
+       codec->spdif_status = ucontrol->value.iec958.status[0] |
+               ((unsigned int)ucontrol->value.iec958.status[1] << 8) |
+               ((unsigned int)ucontrol->value.iec958.status[2] << 16) |
+               ((unsigned int)ucontrol->value.iec958.status[3] << 24);
+       val = convert_from_spdif_status(codec->spdif_status);
+       val |= codec->spdif_ctls & 1;
+       change = codec->spdif_ctls != val;
+       codec->spdif_ctls = val;
+
+       if (change || codec->in_resume) {
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff);
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, val >> 8);
+       }
+
+       up(&codec->spdif_mutex);
+       return change;
+}
+
+static int snd_hda_spdif_out_switch_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_hda_spdif_out_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = codec->spdif_ctls & 1;
+       return 0;
+}
+
+static int snd_hda_spdif_out_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = kcontrol->private_value;
+       unsigned short val;
+       int change;
+
+       down(&codec->spdif_mutex);
+       val = codec->spdif_ctls & ~1;
+       if (ucontrol->value.integer.value[0])
+               val |= 1;
+       change = codec->spdif_ctls != val;
+       if (change || codec->in_resume) {
+               codec->spdif_ctls = val;
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val & 0xff);
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                                   AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT |
+                                   AC_AMP_SET_OUTPUT | ((val & 1) ? 0 : 0x80));
+       }
+       up(&codec->spdif_mutex);
+       return change;
+}
+
+static snd_kcontrol_new_t dig_mixes[] = {
+       {
+               .access = SNDRV_CTL_ELEM_ACCESS_READ,
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+               .info = snd_hda_spdif_mask_info,
+               .get = snd_hda_spdif_cmask_get,
+       },
+       {
+               .access = SNDRV_CTL_ELEM_ACCESS_READ,
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+               .info = snd_hda_spdif_mask_info,
+               .get = snd_hda_spdif_pmask_get,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+               .info = snd_hda_spdif_mask_info,
+               .get = snd_hda_spdif_default_get,
+               .put = snd_hda_spdif_default_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
+               .info = snd_hda_spdif_out_switch_info,
+               .get = snd_hda_spdif_out_switch_get,
+               .put = snd_hda_spdif_out_switch_put,
+       },
+       { } /* end */
+};
+
+/**
+ * snd_hda_create_spdif_out_ctls - create Output SPDIF-related controls
+ * @codec: the HDA codec
+ * @nid: audio out widget NID
+ *
+ * Creates controls related with the SPDIF output.
+ * Called from each patch supporting the SPDIF out.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
+{
+       int err;
+       snd_kcontrol_t *kctl;
+       snd_kcontrol_new_t *dig_mix;
+
+       for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
+               kctl = snd_ctl_new1(dig_mix, codec);
+               kctl->private_value = nid;
+               if ((err = snd_ctl_add(codec->bus->card, kctl)) < 0)
+                       return err;
+       }
+       codec->spdif_ctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
+       codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
+       return 0;
+}
+
+/*
+ * SPDIF input
+ */
+
+#define snd_hda_spdif_in_switch_info   snd_hda_spdif_out_switch_info
+
+static int snd_hda_spdif_in_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = codec->spdif_in_enable;
+       return 0;
+}
+
+static int snd_hda_spdif_in_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = kcontrol->private_value;
+       unsigned int val = !!ucontrol->value.integer.value[0];
+       int change;
+
+       down(&codec->spdif_mutex);
+       change = codec->spdif_in_enable != val;
+       if (change || codec->in_resume) {
+               codec->spdif_in_enable = val;
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, val);
+       }
+       up(&codec->spdif_mutex);
+       return change;
+}
+
+static int snd_hda_spdif_in_status_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = kcontrol->private_value;
+       unsigned short val;
+       unsigned int sbits;
+
+       val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
+       sbits = convert_to_spdif_status(val);
+       ucontrol->value.iec958.status[0] = sbits;
+       ucontrol->value.iec958.status[1] = sbits >> 8;
+       ucontrol->value.iec958.status[2] = sbits >> 16;
+       ucontrol->value.iec958.status[3] = sbits >> 24;
+       return 0;
+}
+
+static snd_kcontrol_new_t dig_in_ctls[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH),
+               .info = snd_hda_spdif_in_switch_info,
+               .get = snd_hda_spdif_in_switch_get,
+               .put = snd_hda_spdif_in_switch_put,
+       },
+       {
+               .access = SNDRV_CTL_ELEM_ACCESS_READ,
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
+               .info = snd_hda_spdif_mask_info,
+               .get = snd_hda_spdif_in_status_get,
+       },
+       { } /* end */
+};
+
+/**
+ * snd_hda_create_spdif_in_ctls - create Input SPDIF-related controls
+ * @codec: the HDA codec
+ * @nid: audio in widget NID
+ *
+ * Creates controls related with the SPDIF input.
+ * Called from each patch supporting the SPDIF in.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
+{
+       int err;
+       snd_kcontrol_t *kctl;
+       snd_kcontrol_new_t *dig_mix;
+
+       for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
+               kctl = snd_ctl_new1(dig_mix, codec);
+               kctl->private_value = nid;
+               if ((err = snd_ctl_add(codec->bus->card, kctl)) < 0)
+                       return err;
+       }
+       codec->spdif_in_enable = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0) & 1;
+       return 0;
+}
+
+
+/**
+ * snd_hda_build_controls - build mixer controls
+ * @bus: the BUS
+ *
+ * Creates mixer controls for each codec included in the bus.
+ *
+ * Returns 0 if successful, otherwise a negative error code.
+ */
+int snd_hda_build_controls(struct hda_bus *bus)
+{
+       struct list_head *p;
+
+       /* build controls */
+       list_for_each(p, &bus->codec_list) {
+               struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+               int err;
+               if (! codec->patch_ops.build_controls)
+                       continue;
+               err = codec->patch_ops.build_controls(codec);
+               if (err < 0)
+                       return err;
+       }
+
+       /* initialize */
+       list_for_each(p, &bus->codec_list) {
+               struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+               int err;
+               if (! codec->patch_ops.init)
+                       continue;
+               err = codec->patch_ops.init(codec);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+
+/*
+ * stream formats
+ */
+static unsigned int rate_bits[][3] = {
+       /* rate in Hz, ALSA rate bitmask, HDA format value */
+       { 8000, SNDRV_PCM_RATE_8000, 0x0500 }, /* 1/6 x 48 */
+       { 11025, SNDRV_PCM_RATE_11025, 0x4300 }, /* 1/4 x 44 */
+       { 16000, SNDRV_PCM_RATE_16000, 0x0200 }, /* 1/3 x 48 */
+       { 22050, SNDRV_PCM_RATE_22050, 0x4100 }, /* 1/2 x 44 */
+       { 32000, SNDRV_PCM_RATE_32000, 0x0a00 }, /* 2/3 x 48 */
+       { 44100, SNDRV_PCM_RATE_44100, 0x4000 }, /* 44 */
+       { 48000, SNDRV_PCM_RATE_48000, 0x0000 }, /* 48 */
+       { 88200, SNDRV_PCM_RATE_88200, 0x4800 }, /* 2 x 44 */
+       { 96000, SNDRV_PCM_RATE_96000, 0x0800 }, /* 2 x 48 */
+       { 176400, SNDRV_PCM_RATE_176400, 0x5800 },/* 4 x 44 */
+       { 192000, SNDRV_PCM_RATE_192000, 0x1800 }, /* 4 x 48 */
+       { 0 }
+};
+
+/**
+ * snd_hda_calc_stream_format - calculate format bitset
+ * @rate: the sample rate
+ * @channels: the number of channels
+ * @format: the PCM format (SNDRV_PCM_FORMAT_XXX)
+ * @maxbps: the max. bps
+ *
+ * Calculate the format bitset from the given rate, channels and th PCM format.
+ *
+ * Return zero if invalid.
+ */
+unsigned int snd_hda_calc_stream_format(unsigned int rate,
+                                       unsigned int channels,
+                                       unsigned int format,
+                                       unsigned int maxbps)
+{
+       int i;
+       unsigned int val = 0;
+
+       for (i = 0; rate_bits[i][0]; i++)
+               if (rate_bits[i][0] == rate) {
+                       val = rate_bits[i][2];
+                       break;
+               }
+       if (! rate_bits[i][0]) {
+               snd_printdd("invalid rate %d\n", rate);
+               return 0;
+       }
+
+       if (channels == 0 || channels > 8) {
+               snd_printdd("invalid channels %d\n", channels);
+               return 0;
+       }
+       val |= channels - 1;
+
+       switch (snd_pcm_format_width(format)) {
+       case 8:  val |= 0x00; break;
+       case 16: val |= 0x10; break;
+       case 20:
+       case 24:
+       case 32:
+               if (maxbps >= 32)
+                       val |= 0x40;
+               else if (maxbps >= 24)
+                       val |= 0x30;
+               else
+                       val |= 0x20;
+               break;
+       default:
+               snd_printdd("invalid format width %d\n", snd_pcm_format_width(format));
+               return 0;
+       }
+
+       return val;
+}
+
+/**
+ * snd_hda_query_supported_pcm - query the supported PCM rates and formats
+ * @codec: the HDA codec
+ * @nid: NID to query
+ * @ratesp: the pointer to store the detected rate bitflags
+ * @formatsp: the pointer to store the detected formats
+ * @bpsp: the pointer to store the detected format widths
+ *
+ * Queries the supported PCM rates and formats.  The NULL @ratesp, @formatsp
+ * or @bsps argument is ignored.
+ *
+ * Returns 0 if successful, otherwise a negative error code.
+ */
+int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
+                               u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
+{
+       int i;
+       unsigned int val, streams;
+
+       val = 0;
+       if (nid != codec->afg &&
+           snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) {
+               val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
+               if (val == -1)
+                       return -EIO;
+       }
+       if (! val)
+               val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
+
+       if (ratesp) {
+               u32 rates = 0;
+               for (i = 0; rate_bits[i][0]; i++) {
+                       if (val & (1 << i))
+                               rates |= rate_bits[i][1];
+               }
+               *ratesp = rates;
+       }
+
+       if (formatsp || bpsp) {
+               u64 formats = 0;
+               unsigned int bps;
+               unsigned int wcaps;
+
+               wcaps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+               streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+               if (streams == -1)
+                       return -EIO;
+               if (! streams) {
+                       streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
+                       if (streams == -1)
+                               return -EIO;
+               }
+
+               bps = 0;
+               if (streams & AC_SUPFMT_PCM) {
+                       if (val & AC_SUPPCM_BITS_8) {
+                               formats |= SNDRV_PCM_FMTBIT_U8;
+                               bps = 8;
+                       }
+                       if (val & AC_SUPPCM_BITS_16) {
+                               formats |= SNDRV_PCM_FMTBIT_S16_LE;
+                               bps = 16;
+                       }
+                       if (wcaps & AC_WCAP_DIGITAL) {
+                               if (val & AC_SUPPCM_BITS_32)
+                                       formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+                               if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24))
+                                       formats |= SNDRV_PCM_FMTBIT_S32_LE;
+                               if (val & AC_SUPPCM_BITS_24)
+                                       bps = 24;
+                               else if (val & AC_SUPPCM_BITS_20)
+                                       bps = 20;
+                       } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24|AC_SUPPCM_BITS_32)) {
+                               formats |= SNDRV_PCM_FMTBIT_S32_LE;
+                               if (val & AC_SUPPCM_BITS_32)
+                                       bps = 32;
+                               else if (val & AC_SUPPCM_BITS_20)
+                                       bps = 20;
+                               else if (val & AC_SUPPCM_BITS_24)
+                                       bps = 24;
+                       }
+               }
+               else if (streams == AC_SUPFMT_FLOAT32) { /* should be exclusive */
+                       formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
+                       bps = 32;
+               } else if (streams == AC_SUPFMT_AC3) { /* should be exclusive */
+                       /* temporary hack: we have still no proper support
+                        * for the direct AC3 stream...
+                        */
+                       formats |= SNDRV_PCM_FMTBIT_U8;
+                       bps = 8;
+               }
+               if (formatsp)
+                       *formatsp = formats;
+               if (bpsp)
+                       *bpsp = bps;
+       }
+
+       return 0;
+}
+
+/**
+ * snd_hda_is_supported_format - check whether the given node supports the format val
+ *
+ * Returns 1 if supported, 0 if not.
+ */
+int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
+                               unsigned int format)
+{
+       int i;
+       unsigned int val = 0, rate, stream;
+
+       if (nid != codec->afg &&
+           snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) {
+               val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
+               if (val == -1)
+                       return 0;
+       }
+       if (! val) {
+               val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
+               if (val == -1)
+                       return 0;
+       }
+
+       rate = format & 0xff00;
+       for (i = 0; rate_bits[i][0]; i++)
+               if (rate_bits[i][2] == rate) {
+                       if (val & (1 << i))
+                               break;
+                       return 0;
+               }
+       if (! rate_bits[i][0])
+               return 0;
+
+       stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+       if (stream == -1)
+               return 0;
+       if (! stream && nid != codec->afg)
+               stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
+       if (! stream || stream == -1)
+               return 0;
+
+       if (stream & AC_SUPFMT_PCM) {
+               switch (format & 0xf0) {
+               case 0x00:
+                       if (! (val & AC_SUPPCM_BITS_8))
+                               return 0;
+                       break;
+               case 0x10:
+                       if (! (val & AC_SUPPCM_BITS_16))
+                               return 0;
+                       break;
+               case 0x20:
+                       if (! (val & AC_SUPPCM_BITS_20))
+                               return 0;
+                       break;
+               case 0x30:
+                       if (! (val & AC_SUPPCM_BITS_24))
+                               return 0;
+                       break;
+               case 0x40:
+                       if (! (val & AC_SUPPCM_BITS_32))
+                               return 0;
+                       break;
+               default:
+                       return 0;
+               }
+       } else {
+               /* FIXME: check for float32 and AC3? */
+       }
+
+       return 1;
+}
+
+/*
+ * PCM stuff
+ */
+static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     snd_pcm_substream_t *substream)
+{
+       return 0;
+}
+
+static int hda_pcm_default_prepare(struct hda_pcm_stream *hinfo,
+                                  struct hda_codec *codec,
+                                  unsigned int stream_tag,
+                                  unsigned int format,
+                                  snd_pcm_substream_t *substream)
+{
+       snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+       return 0;
+}
+
+static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
+                                  struct hda_codec *codec,
+                                  snd_pcm_substream_t *substream)
+{
+       snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0);
+       return 0;
+}
+
+static int set_pcm_default_values(struct hda_codec *codec, struct hda_pcm_stream *info)
+{
+       if (info->nid) {
+               /* query support PCM information from the given NID */
+               if (! info->rates || ! info->formats)
+                       snd_hda_query_supported_pcm(codec, info->nid,
+                                                   info->rates ? NULL : &info->rates,
+                                                   info->formats ? NULL : &info->formats,
+                                                   info->maxbps ? NULL : &info->maxbps);
+       }
+       if (info->ops.open == NULL)
+               info->ops.open = hda_pcm_default_open_close;
+       if (info->ops.close == NULL)
+               info->ops.close = hda_pcm_default_open_close;
+       if (info->ops.prepare == NULL) {
+               snd_assert(info->nid, return -EINVAL);
+               info->ops.prepare = hda_pcm_default_prepare;
+       }
+       if (info->ops.prepare == NULL) {
+               snd_assert(info->nid, return -EINVAL);
+               info->ops.prepare = hda_pcm_default_prepare;
+       }
+       if (info->ops.cleanup == NULL) {
+               snd_assert(info->nid, return -EINVAL);
+               info->ops.cleanup = hda_pcm_default_cleanup;
+       }
+       return 0;
+}
+
+/**
+ * snd_hda_build_pcms - build PCM information
+ * @bus: the BUS
+ *
+ * Create PCM information for each codec included in the bus.
+ *
+ * The build_pcms codec patch is requested to set up codec->num_pcms and
+ * codec->pcm_info properly.  The array is referred by the top-level driver
+ * to create its PCM instances.
+ * The allocated codec->pcm_info should be released in codec->patch_ops.free
+ * callback.
+ *
+ * At least, substreams, channels_min and channels_max must be filled for
+ * each stream.  substreams = 0 indicates that the stream doesn't exist.
+ * When rates and/or formats are zero, the supported values are queried
+ * from the given nid.  The nid is used also by the default ops.prepare
+ * and ops.cleanup callbacks.
+ *
+ * The driver needs to call ops.open in its open callback.  Similarly,
+ * ops.close is supposed to be called in the close callback.
+ * ops.prepare should be called in the prepare or hw_params callback
+ * with the proper parameters for set up.
+ * ops.cleanup should be called in hw_free for clean up of streams.
+ *
+ * This function returns 0 if successfull, or a negative error code.
+ */
+int snd_hda_build_pcms(struct hda_bus *bus)
+{
+       struct list_head *p;
+
+       list_for_each(p, &bus->codec_list) {
+               struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+               unsigned int pcm, s;
+               int err;
+               if (! codec->patch_ops.build_pcms)
+                       continue;
+               err = codec->patch_ops.build_pcms(codec);
+               if (err < 0)
+                       return err;
+               for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+                       for (s = 0; s < 2; s++) {
+                               struct hda_pcm_stream *info;
+                               info = &codec->pcm_info[pcm].stream[s];
+                               if (! info->substreams)
+                                       continue;
+                               err = set_pcm_default_values(codec, info);
+                               if (err < 0)
+                                       return err;
+                       }
+               }
+       }
+       return 0;
+}
+
+
+/**
+ * snd_hda_check_board_config - compare the current codec with the config table
+ * @codec: the HDA codec
+ * @tbl: configuration table, terminated by null entries
+ *
+ * Compares the modelname or PCI subsystem id of the current codec with the
+ * given configuration table.  If a matching entry is found, returns its
+ * config value (supposed to be 0 or positive).
+ *
+ * If no entries are matching, the function returns a negative value.
+ */
+int snd_hda_check_board_config(struct hda_codec *codec, struct hda_board_config *tbl)
+{
+       struct hda_board_config *c;
+
+       if (codec->bus->modelname) {
+               for (c = tbl; c->modelname || c->pci_vendor; c++) {
+                       if (c->modelname &&
+                           ! strcmp(codec->bus->modelname, c->modelname)) {
+                               snd_printd(KERN_INFO "hda_codec: model '%s' is selected\n", c->modelname);
+                               return c->config;
+                       }
+               }
+       }
+
+       if (codec->bus->pci) {
+               u16 subsystem_vendor, subsystem_device;
+               pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
+               pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_ID, &subsystem_device);
+               for (c = tbl; c->modelname || c->pci_vendor; c++) {
+                       if (c->pci_vendor == subsystem_vendor &&
+                           c->pci_device == subsystem_device)
+                               return c->config;
+               }
+       }
+       return -1;
+}
+
+/**
+ * snd_hda_add_new_ctls - create controls from the array
+ * @codec: the HDA codec
+ * @knew: the array of snd_kcontrol_new_t
+ *
+ * This helper function creates and add new controls in the given array.
+ * The array must be terminated with an empty entry as terminator.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hda_add_new_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew)
+{
+       int err;
+
+       for (; knew->name; knew++) {
+               err = snd_ctl_add(codec->bus->card, snd_ctl_new1(knew, codec));
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+
+/*
+ * input MUX helper
+ */
+int snd_hda_input_mux_info(const struct hda_input_mux *imux, snd_ctl_elem_info_t *uinfo)
+{
+       unsigned int index;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = imux->num_items;
+       index = uinfo->value.enumerated.item;
+       if (index >= imux->num_items)
+               index = imux->num_items - 1;
+       strcpy(uinfo->value.enumerated.name, imux->items[index].label);
+       return 0;
+}
+
+int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux,
+                         snd_ctl_elem_value_t *ucontrol, hda_nid_t nid,
+                         unsigned int *cur_val)
+{
+       unsigned int idx;
+
+       idx = ucontrol->value.enumerated.item[0];
+       if (idx >= imux->num_items)
+               idx = imux->num_items - 1;
+       if (*cur_val == idx && ! codec->in_resume)
+               return 0;
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
+                           imux->items[idx].index);
+       *cur_val = idx;
+       return 1;
+}
+
+
+/*
+ * Multi-channel / digital-out PCM helper functions
+ */
+
+/*
+ * open the digital out in the exclusive mode
+ */
+int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout)
+{
+       down(&codec->spdif_mutex);
+       if (mout->dig_out_used) {
+               up(&codec->spdif_mutex);
+               return -EBUSY; /* already being used */
+       }
+       mout->dig_out_used = HDA_DIG_EXCLUSIVE;
+       up(&codec->spdif_mutex);
+       return 0;
+}
+
+/*
+ * release the digital out
+ */
+int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout)
+{
+       down(&codec->spdif_mutex);
+       mout->dig_out_used = 0;
+       up(&codec->spdif_mutex);
+       return 0;
+}
+
+/*
+ * set up more restrictions for analog out
+ */
+int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout,
+                                 snd_pcm_substream_t *substream)
+{
+       substream->runtime->hw.channels_max = mout->max_channels;
+       return snd_pcm_hw_constraint_step(substream->runtime, 0,
+                                         SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+}
+
+/*
+ * set up the i/o for analog out
+ * when the digital out is available, copy the front out to digital out, too.
+ */
+int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout,
+                                    unsigned int stream_tag,
+                                    unsigned int format,
+                                    snd_pcm_substream_t *substream)
+{
+       hda_nid_t *nids = mout->dac_nids;
+       int chs = substream->runtime->channels;
+       int i;
+
+       down(&codec->spdif_mutex);
+       if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
+               if (chs == 2 &&
+                   snd_hda_is_supported_format(codec, mout->dig_out_nid, format) &&
+                   ! (codec->spdif_status & IEC958_AES0_NONAUDIO)) {
+                       mout->dig_out_used = HDA_DIG_ANALOG_DUP;
+                       /* setup digital receiver */
+                       snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
+                                                  stream_tag, 0, format);
+               } else {
+                       mout->dig_out_used = 0;
+                       snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
+               }
+       }
+       up(&codec->spdif_mutex);
+
+       /* front */
+       snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format);
+       if (mout->hp_nid)
+               /* headphone out will just decode front left/right (stereo) */
+               snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format);
+       /* surrounds */
+       for (i = 1; i < mout->num_dacs; i++) {
+               if (i == HDA_REAR && chs == 2) /* copy front to rear */
+                       snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0, format);
+               else if (chs >= (i + 1) * 2) /* independent out */
+                       snd_hda_codec_setup_stream(codec, nids[i], stream_tag, i * 2,
+                                                  format);
+       }
+       return 0;
+}
+
+/*
+ * clean up the setting for analog out
+ */
+int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout)
+{
+       hda_nid_t *nids = mout->dac_nids;
+       int i;
+
+       for (i = 0; i < mout->num_dacs; i++)
+               snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
+       if (mout->hp_nid)
+               snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0);
+       down(&codec->spdif_mutex);
+       if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
+               snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
+               mout->dig_out_used = 0;
+       }
+       up(&codec->spdif_mutex);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+
+/**
+ * snd_hda_suspend - suspend the codecs
+ * @bus: the HDA bus
+ * @state: suspsend state
+ *
+ * Returns 0 if successful.
+ */
+int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
+{
+       struct list_head *p;
+
+       /* FIXME: should handle power widget capabilities */
+       list_for_each(p, &bus->codec_list) {
+               struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+               if (codec->patch_ops.suspend)
+                       codec->patch_ops.suspend(codec, state);
+       }
+       return 0;
+}
+
+/**
+ * snd_hda_resume - resume the codecs
+ * @bus: the HDA bus
+ * @state: resume state
+ *
+ * Returns 0 if successful.
+ */
+int snd_hda_resume(struct hda_bus *bus)
+{
+       struct list_head *p;
+
+       list_for_each(p, &bus->codec_list) {
+               struct hda_codec *codec = list_entry(p, struct hda_codec, list);
+               if (codec->patch_ops.resume)
+                       codec->patch_ops.resume(codec);
+       }
+       return 0;
+}
+
+/**
+ * snd_hda_resume_ctls - resume controls in the new control list
+ * @codec: the HDA codec
+ * @knew: the array of snd_kcontrol_new_t
+ *
+ * This function resumes the mixer controls in the snd_kcontrol_new_t array,
+ * originally for snd_hda_add_new_ctls().
+ * The array must be terminated with an empty entry as terminator.
+ */
+int snd_hda_resume_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew)
+{
+       snd_ctl_elem_value_t *val;
+
+       val = kmalloc(sizeof(*val), GFP_KERNEL);
+       if (! val)
+               return -ENOMEM;
+       codec->in_resume = 1;
+       for (; knew->name; knew++) {
+               int i, count;
+               count = knew->count ? knew->count : 1;
+               for (i = 0; i < count; i++) {
+                       memset(val, 0, sizeof(*val));
+                       val->id.iface = knew->iface;
+                       val->id.device = knew->device;
+                       val->id.subdevice = knew->subdevice;
+                       strcpy(val->id.name, knew->name);
+                       val->id.index = knew->index ? knew->index : i;
+                       /* Assume that get callback reads only from cache,
+                        * not accessing to the real hardware
+                        */
+                       if (snd_ctl_elem_read(codec->bus->card, val) < 0)
+                               continue;
+                       snd_ctl_elem_write(codec->bus->card, NULL, val);
+               }
+       }
+       codec->in_resume = 0;
+       kfree(val);
+       return 0;
+}
+
+/**
+ * snd_hda_resume_spdif_out - resume the digital out
+ * @codec: the HDA codec
+ */
+int snd_hda_resume_spdif_out(struct hda_codec *codec)
+{
+       return snd_hda_resume_ctls(codec, dig_mixes);
+}
+
+/**
+ * snd_hda_resume_spdif_in - resume the digital in
+ * @codec: the HDA codec
+ */
+int snd_hda_resume_spdif_in(struct hda_codec *codec)
+{
+       return snd_hda_resume_ctls(codec, dig_in_ctls);
+}
+#endif
+
+/*
+ * symbols exported for controller modules
+ */
+EXPORT_SYMBOL(snd_hda_codec_read);
+EXPORT_SYMBOL(snd_hda_codec_write);
+EXPORT_SYMBOL(snd_hda_sequence_write);
+EXPORT_SYMBOL(snd_hda_get_sub_nodes);
+EXPORT_SYMBOL(snd_hda_queue_unsol_event);
+EXPORT_SYMBOL(snd_hda_bus_new);
+EXPORT_SYMBOL(snd_hda_codec_new);
+EXPORT_SYMBOL(snd_hda_codec_setup_stream);
+EXPORT_SYMBOL(snd_hda_calc_stream_format);
+EXPORT_SYMBOL(snd_hda_build_pcms);
+EXPORT_SYMBOL(snd_hda_build_controls);
+#ifdef CONFIG_PM
+EXPORT_SYMBOL(snd_hda_suspend);
+EXPORT_SYMBOL(snd_hda_resume);
+#endif
+
+/*
+ *  INIT part
+ */
+
+static int __init alsa_hda_init(void)
+{
+       return 0;
+}
+
+static void __exit alsa_hda_exit(void)
+{
+}
+
+module_init(alsa_hda_init)
+module_exit(alsa_hda_exit)
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
new file mode 100644 (file)
index 0000000..c9e9dc9
--- /dev/null
@@ -0,0 +1,604 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Copyright (c) 2004 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.
+ */
+
+#ifndef __SOUND_HDA_CODEC_H
+#define __SOUND_HDA_CODEC_H
+
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+
+/*
+ * nodes
+ */
+#define        AC_NODE_ROOT            0x00
+
+/*
+ * function group types
+ */
+enum {
+       AC_GRP_AUDIO_FUNCTION = 0x01,
+       AC_GRP_MODEM_FUNCTION = 0x02,
+};
+       
+/*
+ * widget types
+ */
+enum {
+       AC_WID_AUD_OUT,         /* Audio Out */
+       AC_WID_AUD_IN,          /* Audio In */
+       AC_WID_AUD_MIX,         /* Audio Mixer */
+       AC_WID_AUD_SEL,         /* Audio Selector */
+       AC_WID_PIN,             /* Pin Complex */
+       AC_WID_POWER,           /* Power */
+       AC_WID_VOL_KNB,         /* Volume Knob */
+       AC_WID_BEEP,            /* Beep Generator */
+       AC_WID_VENDOR = 0x0f    /* Vendor specific */
+};
+
+/*
+ * GET verbs
+ */
+#define AC_VERB_GET_STREAM_FORMAT              0x0a00
+#define AC_VERB_GET_AMP_GAIN_MUTE              0x0b00
+#define AC_VERB_GET_PROC_COEF                  0x0c00
+#define AC_VERB_GET_COEF_INDEX                 0x0d00
+#define AC_VERB_PARAMETERS                     0x0f00
+#define AC_VERB_GET_CONNECT_SEL                        0x0f01
+#define AC_VERB_GET_CONNECT_LIST               0x0f02
+#define AC_VERB_GET_PROC_STATE                 0x0f03
+#define AC_VERB_GET_SDI_SELECT                 0x0f04
+#define AC_VERB_GET_POWER_STATE                        0x0f05
+#define AC_VERB_GET_CONV                       0x0f06
+#define AC_VERB_GET_PIN_WIDGET_CONTROL         0x0f07
+#define AC_VERB_GET_UNSOLICITED_RESPONSE       0x0f08
+#define AC_VERB_GET_PIN_SENSE                  0x0f09
+#define AC_VERB_GET_BEEP_CONTROL               0x0f0a
+#define AC_VERB_GET_EAPD_BTLENABLE             0x0f0c
+#define AC_VERB_GET_DIGI_CONVERT               0x0f0d
+#define AC_VERB_GET_VOLUME_KNOB_CONTROL                0x0f0f
+/* f10-f1a: GPIO */
+#define AC_VERB_GET_CONFIG_DEFAULT             0x0f1c
+
+/*
+ * SET verbs
+ */
+#define AC_VERB_SET_STREAM_FORMAT              0x200
+#define AC_VERB_SET_AMP_GAIN_MUTE              0x300
+#define AC_VERB_SET_PROC_COEF                  0x400
+#define AC_VERB_SET_COEF_INDEX                 0x500
+#define AC_VERB_SET_CONNECT_SEL                        0x701
+#define AC_VERB_SET_PROC_STATE                 0x703
+#define AC_VERB_SET_SDI_SELECT                 0x704
+#define AC_VERB_SET_POWER_STATE                        0x705
+#define AC_VERB_SET_CHANNEL_STREAMID           0x706
+#define AC_VERB_SET_PIN_WIDGET_CONTROL         0x707
+#define AC_VERB_SET_UNSOLICITED_ENABLE         0x708
+#define AC_VERB_SET_PIN_SENSE                  0x709
+#define AC_VERB_SET_BEEP_CONTROL               0x70a
+#define AC_VERB_SET_EAPD_BTLENALBE             0x70c
+#define AC_VERB_SET_DIGI_CONVERT_1             0x70d
+#define AC_VERB_SET_DIGI_CONVERT_2             0x70e
+#define AC_VERB_SET_VOLUME_KNOB_CONTROL                0x70f
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0     0x71c
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1     0x71d
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2     0x71e
+#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3     0x71f
+#define AC_VERB_SET_CODEC_RESET                        0x7ff
+
+/*
+ * Parameter IDs
+ */
+#define AC_PAR_VENDOR_ID               0x00
+#define AC_PAR_SUBSYSTEM_ID            0x01
+#define AC_PAR_REV_ID                  0x02
+#define AC_PAR_NODE_COUNT              0x04
+#define AC_PAR_FUNCTION_TYPE           0x05
+#define AC_PAR_AUDIO_FG_CAP            0x08
+#define AC_PAR_AUDIO_WIDGET_CAP                0x09
+#define AC_PAR_PCM                     0x0a
+#define AC_PAR_STREAM                  0x0b
+#define AC_PAR_PIN_CAP                 0x0c
+#define AC_PAR_AMP_IN_CAP              0x0d
+#define AC_PAR_CONNLIST_LEN            0x0e
+#define AC_PAR_POWER_STATE             0x0f
+#define AC_PAR_PROC_CAP                        0x10
+#define AC_PAR_GPIO_CAP                        0x11
+#define AC_PAR_AMP_OUT_CAP             0x12
+
+/*
+ * AC_VERB_PARAMETERS results (32bit)
+ */
+
+/* Function Group Type */
+#define AC_FGT_TYPE                    (0xff<<0)
+#define AC_FGT_TYPE_SHIFT              0
+#define AC_FGT_UNSOL_CAP               (1<<8)
+
+/* Audio Function Group Capabilities */
+#define AC_AFG_OUT_DELAY               (0xf<<0)
+#define AC_AFG_IN_DELAY                        (0xf<<8)
+#define AC_AFG_BEEP_GEN                        (1<<16)
+
+/* Audio Widget Capabilities */
+#define AC_WCAP_STEREO                 (1<<0)  /* stereo I/O */
+#define AC_WCAP_IN_AMP                 (1<<1)  /* AMP-in present */
+#define AC_WCAP_OUT_AMP                        (1<<2)  /* AMP-out present */
+#define AC_WCAP_AMP_OVRD               (1<<3)  /* AMP-parameter override */
+#define AC_WCAP_FORMAT_OVRD            (1<<4)  /* format override */
+#define AC_WCAP_STRIPE                 (1<<5)  /* stripe */
+#define AC_WCAP_PROC_WID               (1<<6)  /* Proc Widget */
+#define AC_WCAP_UNSOL_CAP              (1<<7)  /* Unsol capable */
+#define AC_WCAP_CONN_LIST              (1<<8)  /* connection list */
+#define AC_WCAP_DIGITAL                        (1<<9)  /* digital I/O */
+#define AC_WCAP_POWER                  (1<<10) /* power control */
+#define AC_WCAP_LR_SWAP                        (1<<11) /* L/R swap */
+#define AC_WCAP_DELAY                  (0xf<<16)
+#define AC_WCAP_DELAY_SHIFT            16
+#define AC_WCAP_TYPE                   (0xf<<20)
+#define AC_WCAP_TYPE_SHIFT             20
+
+/* supported PCM rates and bits */
+#define AC_SUPPCM_RATES                        (0xfff << 0)
+#define AC_SUPPCM_BITS_8               (1<<16)
+#define AC_SUPPCM_BITS_16              (1<<17)
+#define AC_SUPPCM_BITS_20              (1<<18)
+#define AC_SUPPCM_BITS_24              (1<<19)
+#define AC_SUPPCM_BITS_32              (1<<20)
+
+/* supported PCM stream format */
+#define AC_SUPFMT_PCM                  (1<<0)
+#define AC_SUPFMT_FLOAT32              (1<<1)
+#define AC_SUPFMT_AC3                  (1<<2)
+
+/* Pin widget capabilies */
+#define AC_PINCAP_IMP_SENSE            (1<<0)  /* impedance sense capable */
+#define AC_PINCAP_TRIG_REQ             (1<<1)  /* trigger required */
+#define AC_PINCAP_PRES_DETECT          (1<<2)  /* presence detect capable */
+#define AC_PINCAP_HP_DRV               (1<<3)  /* headphone drive capable */
+#define AC_PINCAP_OUT                  (1<<4)  /* output capable */
+#define AC_PINCAP_IN                   (1<<5)  /* input capable */
+#define AC_PINCAP_BALANCE              (1<<6)  /* balanced I/O capable */
+#define AC_PINCAP_VREF                 (7<<8)
+#define AC_PINCAP_VREF_SHIFT           8
+#define AC_PINCAP_EAPD                 (1<<16) /* EAPD capable */
+/* Vref status (used in pin cap and pin ctl) */
+#define AC_PIN_VREF_HIZ                        (1<<0)  /* Hi-Z */
+#define AC_PIN_VREF_50                 (1<<1)  /* 50% */
+#define AC_PIN_VREF_GRD                        (1<<2)  /* ground */
+#define AC_PIN_VREF_80                 (1<<4)  /* 80% */
+#define AC_PIN_VREF_100                        (1<<5)  /* 100% */
+
+
+/* Amplifier capabilities */
+#define AC_AMPCAP_OFFSET               (0x7f<<0)  /* 0dB offset */
+#define AC_AMPCAP_OFFSET_SHIFT         0
+#define AC_AMPCAP_NUM_STEPS            (0x7f<<8)  /* number of steps */
+#define AC_AMPCAP_NUM_STEPS_SHIFT      8
+#define AC_AMPCAP_STEP_SIZE            (0x7f<<16) /* step size 0-32dB in 0.25dB */
+#define AC_AMPCAP_STEP_SIZE_SHIFT      16
+#define AC_AMPCAP_MUTE                 (1<<31)    /* mute capable */
+#define AC_AMPCAP_MUTE_SHIFT           31
+
+/* Connection list */
+#define AC_CLIST_LENGTH                        (0x7f<<0)
+#define AC_CLIST_LONG                  (1<<7)
+
+/* Supported power status */
+#define AC_PWRST_D0SUP                 (1<<0)
+#define AC_PWRST_D1SUP                 (1<<1)
+#define AC_PWRST_D2SUP                 (1<<2)
+#define AC_PWRST_D3SUP                 (1<<3)
+
+/* Processing capabilies */
+#define AC_PCAP_BENIGN                 (1<<0)
+#define AC_PCAP_NUM_COEF               (0xff<<8)
+
+/* Volume knobs capabilities */
+#define AC_KNBCAP_NUM_STEPS            (0x7f<<0)
+#define AC_KNBCAP_DELTA                        (1<<8)
+
+/*
+ * Control Parameters
+ */
+
+/* Amp gain/mute */
+#define AC_AMP_MUTE                    (1<<8)
+#define AC_AMP_GAIN                    (0x7f)
+#define AC_AMP_GET_INDEX               (0xf<<0)
+
+#define AC_AMP_GET_LEFT                        (1<<13)
+#define AC_AMP_GET_RIGHT               (0<<13)
+#define AC_AMP_GET_OUTPUT              (1<<15)
+#define AC_AMP_GET_INPUT               (0<<15)
+
+#define AC_AMP_SET_INDEX               (0xf<<8)
+#define AC_AMP_SET_INDEX_SHIFT         8
+#define AC_AMP_SET_RIGHT               (1<<12)
+#define AC_AMP_SET_LEFT                        (1<<13)
+#define AC_AMP_SET_INPUT               (1<<14)
+#define AC_AMP_SET_OUTPUT              (1<<15)
+
+/* DIGITAL1 bits */
+#define AC_DIG1_ENABLE                 (1<<0)
+#define AC_DIG1_V                      (1<<1)
+#define AC_DIG1_VCFG                   (1<<2)
+#define AC_DIG1_EMPHASIS               (1<<3)
+#define AC_DIG1_COPYRIGHT              (1<<4)
+#define AC_DIG1_NONAUDIO               (1<<5)
+#define AC_DIG1_PROFESSIONAL           (1<<6)
+#define AC_DIG1_LEVEL                  (1<<7)
+
+/* Pin widget control - 8bit */
+#define AC_PINCTL_VREFEN               (0x7<<0)
+#define AC_PINCTL_IN_EN                        (1<<5)
+#define AC_PINCTL_OUT_EN               (1<<6)
+#define AC_PINCTL_HP_EN                        (1<<7)
+
+/* configuration default - 32bit */
+#define AC_DEFCFG_SEQUENCE             (0xf<<0)
+#define AC_DEFCFG_DEF_ASSOC            (0xf<<4)
+#define AC_DEFCFG_MISC                 (0xf<<8)
+#define AC_DEFCFG_COLOR                        (0xf<<12)
+#define AC_DEFCFG_COLOR_SHIFT          12
+#define AC_DEFCFG_CONN_TYPE            (0xf<<16)
+#define AC_DEFCFG_CONN_TYPE_SHIFT      16
+#define AC_DEFCFG_DEVICE               (0xf<<20)
+#define AC_DEFCFG_DEVICE_SHIFT         20
+#define AC_DEFCFG_LOCATION             (0x3f<<24)
+#define AC_DEFCFG_LOCATION_SHIFT       24
+#define AC_DEFCFG_PORT_CONN            (0x3<<30)
+#define AC_DEFCFG_PORT_CONN_SHIFT      30
+
+/* device device types (0x0-0xf) */
+enum {
+       AC_JACK_LINE_OUT,
+       AC_JACK_SPEAKER,
+       AC_JACK_HP_OUT,
+       AC_JACK_CD,
+       AC_JACK_SPDIF_OUT,
+       AC_JACK_DIG_OTHER_OUT,
+       AC_JACK_MODEM_LINE_SIDE,
+       AC_JACK_MODEM_HAND_SIDE,
+       AC_JACK_LINE_IN,
+       AC_JACK_AUX,
+       AC_JACK_MIC_IN,
+       AC_JACK_TELEPHONY,
+       AC_JACK_SPDIF_IN,
+       AC_JACK_DIG_OTHER_IN,
+       AC_JACK_OTHER = 0xf,
+};
+
+/* jack connection types (0x0-0xf) */
+enum {
+       AC_JACK_CONN_UNKNOWN,
+       AC_JACK_CONN_1_8,
+       AC_JACK_CONN_1_4,
+       AC_JACK_CONN_ATAPI,
+       AC_JACK_CONN_RCA,
+       AC_JACK_CONN_OPTICAL,
+       AC_JACK_CONN_OTHER_DIGITAL,
+       AC_JACK_CONN_OTHER_ANALOG,
+       AC_JACK_CONN_DIN,
+       AC_JACK_CONN_XLR,
+       AC_JACK_CONN_RJ11,
+       AC_JACK_CONN_COMB,
+       AC_JACK_CONN_OTHER = 0xf,
+};
+
+/* jack colors (0x0-0xf) */
+enum {
+       AC_JACK_COLOR_UNKNOWN,
+       AC_JACK_COLOR_BLACK,
+       AC_JACK_COLOR_GREY,
+       AC_JACK_COLOR_BLUE,
+       AC_JACK_COLOR_GREEN,
+       AC_JACK_COLOR_RED,
+       AC_JACK_COLOR_ORANGE,
+       AC_JACK_COLOR_YELLOW,
+       AC_JACK_COLOR_PURPLE,
+       AC_JACK_COLOR_PINK,
+       AC_JACK_COLOR_WHITE = 0xe,
+       AC_JACK_COLOR_OTHER,
+};
+
+/* Jack location (0x0-0x3f) */
+/* common case */
+enum {
+       AC_JACK_LOC_NONE,
+       AC_JACK_LOC_REAR,
+       AC_JACK_LOC_FRONT,
+       AC_JACK_LOC_LEFT,
+       AC_JACK_LOC_RIGHT,
+       AC_JACK_LOC_TOP,
+       AC_JACK_LOC_BOTTOM,
+};
+/* bits 4-5 */
+enum {
+       AC_JACK_LOC_EXTERNAL = 0x00,
+       AC_JACK_LOC_INTERNAL = 0x10,
+       AC_JACK_LOC_SEPARATE = 0x20,
+       AC_JACK_LOC_OTHER    = 0x30,
+};
+enum {
+       /* external on primary chasis */
+       AC_JACK_LOC_REAR_PANEL = 0x07,
+       AC_JACK_LOC_DRIVE_BAY,
+       /* internal */
+       AC_JACK_LOC_RISER = 0x17,
+       AC_JACK_LOC_HDMI,
+       AC_JACK_LOC_ATAPI,
+       /* others */
+       AC_JACK_LOC_MOBILE_IN = 0x37,
+       AC_JACK_LOC_MOBILE_OUT,
+};
+
+/* Port connectivity (0-3) */
+enum {
+       AC_JACK_PORT_COMPLEX,
+       AC_JACK_PORT_NONE,
+       AC_JACK_PORT_FIXED,
+       AC_JACK_PORT_BOTH,
+};
+
+/* max. connections to a widget */
+#define HDA_MAX_CONNECTIONS    16
+
+/* max. codec address */
+#define HDA_MAX_CODEC_ADDRESS  0x0f
+
+/*
+ * Structures
+ */
+
+struct hda_bus;
+struct hda_codec;
+struct hda_pcm;
+struct hda_pcm_stream;
+struct hda_bus_unsolicited;
+
+/* NID type */
+typedef u16 hda_nid_t;
+
+/* bus operators */
+struct hda_bus_ops {
+       /* send a single command */
+       int (*command)(struct hda_codec *codec, hda_nid_t nid, int direct,
+                      unsigned int verb, unsigned int parm);
+       /* get a response from the last command */
+       unsigned int (*get_response)(struct hda_codec *codec);
+       /* free the private data */
+       void (*private_free)(struct hda_bus *);
+};
+
+/* template to pass to the bus constructor */
+struct hda_bus_template {
+       void *private_data;
+       struct pci_dev *pci;
+       const char *modelname;
+       struct hda_bus_ops ops;
+};
+
+/*
+ * codec bus
+ *
+ * each controller needs to creata a hda_bus to assign the accessor.
+ * A hda_bus contains several codecs in the list codec_list.
+ */
+struct hda_bus {
+       snd_card_t *card;
+
+       /* copied from template */
+       void *private_data;
+       struct pci_dev *pci;
+       const char *modelname;
+       struct hda_bus_ops ops;
+
+       /* codec linked list */
+       struct list_head codec_list;
+       struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS]; /* caddr -> codec */
+
+       struct semaphore cmd_mutex;
+
+       /* unsolicited event queue */
+       struct hda_bus_unsolicited *unsol;
+
+       snd_info_entry_t *proc;
+};
+
+/*
+ * codec preset
+ *
+ * Known codecs have the patch to build and set up the controls/PCMs
+ * better than the generic parser.
+ */
+struct hda_codec_preset {
+       unsigned int id;
+       unsigned int mask;
+       unsigned int subs;
+       unsigned int subs_mask;
+       unsigned int rev;
+       const char *name;
+       int (*patch)(struct hda_codec *codec);
+};
+       
+/* ops set by the preset patch */
+struct hda_codec_ops {
+       int (*build_controls)(struct hda_codec *codec);
+       int (*build_pcms)(struct hda_codec *codec);
+       int (*init)(struct hda_codec *codec);
+       void (*free)(struct hda_codec *codec);
+       void (*unsol_event)(struct hda_codec *codec, unsigned int res);
+#ifdef CONFIG_PM
+       int (*suspend)(struct hda_codec *codec, pm_message_t state);
+       int (*resume)(struct hda_codec *codec);
+#endif
+};
+
+/* record for amp information cache */
+struct hda_amp_info {
+       u32 key;                /* hash key */
+       u32 amp_caps;           /* amp capabilities */
+       u16 vol[2];             /* current volume & mute*/
+       u16 status;             /* update flag */
+       u16 next;               /* next link */
+};
+
+/* PCM callbacks */
+struct hda_pcm_ops {
+       int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec,
+                   snd_pcm_substream_t *substream);
+       int (*close)(struct hda_pcm_stream *info, struct hda_codec *codec,
+                    snd_pcm_substream_t *substream);
+       int (*prepare)(struct hda_pcm_stream *info, struct hda_codec *codec,
+                      unsigned int stream_tag, unsigned int format,
+                      snd_pcm_substream_t *substream);
+       int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec,
+                      snd_pcm_substream_t *substream);
+};
+
+/* PCM information for each substream */
+struct hda_pcm_stream {
+       unsigned int substreams;        /* number of substreams, 0 = not exist */
+       unsigned int channels_min;      /* min. number of channels */
+       unsigned int channels_max;      /* max. number of channels */
+       hda_nid_t nid;  /* default NID to query rates/formats/bps, or set up */
+       u32 rates;      /* supported rates */
+       u64 formats;    /* supported formats (SNDRV_PCM_FMTBIT_) */
+       unsigned int maxbps;    /* supported max. bit per sample */
+       struct hda_pcm_ops ops;
+};
+
+/* for PCM creation */
+struct hda_pcm {
+       char *name;
+       struct hda_pcm_stream stream[2];
+};
+
+/* codec information */
+struct hda_codec {
+       struct hda_bus *bus;
+       unsigned int addr;      /* codec addr*/
+       struct list_head list;  /* list point */
+
+       hda_nid_t afg;  /* AFG node id */
+
+       /* ids */
+       u32 vendor_id;
+       u32 subsystem_id;
+       u32 revision_id;
+
+       /* detected preset */
+       const struct hda_codec_preset *preset;
+
+       /* set by patch */
+       struct hda_codec_ops patch_ops;
+
+       /* resume phase - all controls should update even if
+        * the values are not changed
+        */
+       unsigned int in_resume;
+
+       /* PCM to create, set by patch_ops.build_pcms callback */
+       unsigned int num_pcms;
+       struct hda_pcm *pcm_info;
+
+       /* codec specific info */
+       void *spec;
+
+       /* hash for amp access */
+       u16 amp_hash[32];
+       int num_amp_entries;
+       struct hda_amp_info amp_info[128]; /* big enough? */
+
+       struct semaphore spdif_mutex;
+       unsigned int spdif_status;      /* IEC958 status bits */
+       unsigned short spdif_ctls;      /* SPDIF control bits */
+       unsigned int spdif_in_enable;   /* SPDIF input enable? */
+};
+
+/* direction */
+enum {
+       HDA_INPUT, HDA_OUTPUT
+};
+
+
+/*
+ * constructors
+ */
+int snd_hda_bus_new(snd_card_t *card, const struct hda_bus_template *temp,
+                   struct hda_bus **busp);
+int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
+                     struct hda_codec **codecp);
+
+/*
+ * low level functions
+ */
+unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int direct,
+                               unsigned int verb, unsigned int parm);
+int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
+                       unsigned int verb, unsigned int parm);
+#define snd_hda_param_read(codec, nid, param) snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param)
+int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id);
+int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns);
+
+struct hda_verb {
+       hda_nid_t nid;
+       u32 verb;
+       u32 param;
+};
+
+void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq);
+
+/* unsolicited event */
+int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
+
+/*
+ * Mixer
+ */
+int snd_hda_build_controls(struct hda_bus *bus);
+
+/*
+ * PCM
+ */
+int snd_hda_build_pcms(struct hda_bus *bus);
+void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag,
+                               int channel_id, int format);
+unsigned int snd_hda_calc_stream_format(unsigned int rate, unsigned int channels,
+                                       unsigned int format, unsigned int maxbps);
+int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
+                               u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
+int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
+                               unsigned int format);
+
+/*
+ * Misc
+ */
+void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
+
+/*
+ * power management
+ */
+#ifdef CONFIG_PM
+int snd_hda_suspend(struct hda_bus *bus, pm_message_t state);
+int snd_hda_resume(struct hda_bus *bus);
+#endif
+
+#endif /* __SOUND_HDA_CODEC_H */
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
new file mode 100644 (file)
index 0000000..69f7b6c
--- /dev/null
@@ -0,0 +1,906 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Generic widget tree parser
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the 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 driver 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/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+/* widget node for parsing */
+struct hda_gnode {
+       hda_nid_t nid;          /* NID of this widget */
+       unsigned short nconns;  /* number of input connections */
+       hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; /* input connections */
+       unsigned int wid_caps;  /* widget capabilities */
+       unsigned char type;     /* widget type */
+       unsigned char pin_ctl;  /* pin controls */
+       unsigned char checked;  /* the flag indicates that the node is already parsed */
+       unsigned int pin_caps;  /* pin widget capabilities */
+       unsigned int def_cfg;   /* default configuration */
+       unsigned int amp_out_caps;      /* AMP out capabilities */
+       unsigned int amp_in_caps;       /* AMP in capabilities */
+       struct list_head list;
+};
+
+/* pathc-specific record */
+struct hda_gspec {
+       struct hda_gnode *dac_node;     /* DAC node */
+       struct hda_gnode *out_pin_node; /* Output pin (Line-Out) node */
+       struct hda_gnode *pcm_vol_node; /* Node for PCM volume */
+       unsigned int pcm_vol_index;     /* connection of PCM volume */
+
+       struct hda_gnode *adc_node;     /* ADC node */
+       struct hda_gnode *cap_vol_node; /* Node for capture volume */
+       unsigned int cur_cap_src;       /* current capture source */
+       struct hda_input_mux input_mux;
+       char cap_labels[HDA_MAX_NUM_INPUTS][16];
+
+       unsigned int def_amp_in_caps;
+       unsigned int def_amp_out_caps;
+
+       struct hda_pcm pcm_rec;         /* PCM information */
+
+       struct list_head nid_list;      /* list of widgets */
+};
+
+/*
+ * retrieve the default device type from the default config value
+ */
+#define get_defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
+#define get_defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT)
+
+/*
+ * destructor
+ */
+static void snd_hda_generic_free(struct hda_codec *codec)
+{
+       struct hda_gspec *spec = codec->spec;
+       struct list_head *p, *n;
+
+       if (! spec)
+               return;
+       /* free all widgets */
+       list_for_each_safe(p, n, &spec->nid_list) {
+               struct hda_gnode *node = list_entry(p, struct hda_gnode, list);
+               kfree(node);
+       }
+       kfree(spec);
+}
+
+
+/*
+ * add a new widget node and read its attributes
+ */
+static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid_t nid)
+{
+       struct hda_gnode *node;
+       int nconns;
+
+       node = kcalloc(1, sizeof(*node), GFP_KERNEL);
+       if (node == NULL)
+               return -ENOMEM;
+       node->nid = nid;
+       nconns = snd_hda_get_connections(codec, nid, node->conn_list, HDA_MAX_CONNECTIONS);
+       if (nconns < 0) {
+               kfree(node);
+               return nconns;
+       }
+       node->nconns = nconns;
+       node->wid_caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+       node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+
+       if (node->type == AC_WID_PIN) {
+               node->pin_caps = snd_hda_param_read(codec, node->nid, AC_PAR_PIN_CAP);
+               node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+               node->def_cfg = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+       }
+
+       if (node->wid_caps & AC_WCAP_OUT_AMP) {
+               if (node->wid_caps & AC_WCAP_AMP_OVRD)
+                       node->amp_out_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_OUT_CAP);
+               if (! node->amp_out_caps)
+                       node->amp_out_caps = spec->def_amp_out_caps;
+       }
+       if (node->wid_caps & AC_WCAP_IN_AMP) {
+               if (node->wid_caps & AC_WCAP_AMP_OVRD)
+                       node->amp_in_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_IN_CAP);
+               if (! node->amp_in_caps)
+                       node->amp_in_caps = spec->def_amp_in_caps;
+       }
+       list_add_tail(&node->list, &spec->nid_list);
+       return 0;
+}
+
+/*
+ * build the AFG subtree
+ */
+static int build_afg_tree(struct hda_codec *codec)
+{
+       struct hda_gspec *spec = codec->spec;
+       int i, nodes, err;
+       hda_nid_t nid;
+
+       snd_assert(spec, return -EINVAL);
+
+       spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP);
+       spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP);
+
+       nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+       if (! nid || nodes < 0) {
+               printk(KERN_ERR "Invalid AFG subtree\n");
+               return -EINVAL;
+       }
+
+       /* parse all nodes belonging to the AFG */
+       for (i = 0; i < nodes; i++, nid++) {
+               if ((err = add_new_node(codec, spec, nid)) < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+
+/*
+ * look for the node record for the given NID
+ */
+/* FIXME: should avoid the braindead linear search */
+static struct hda_gnode *hda_get_node(struct hda_gspec *spec, hda_nid_t nid)
+{
+       struct list_head *p;
+       struct hda_gnode *node;
+
+       list_for_each(p, &spec->nid_list) {
+               node = list_entry(p, struct hda_gnode, list);
+               if (node->nid == nid)
+                       return node;
+       }
+       return NULL;
+}
+
+/*
+ * unmute (and set max vol) the output amplifier
+ */
+static int unmute_output(struct hda_codec *codec, struct hda_gnode *node)
+{
+       unsigned int val, ofs;
+       snd_printdd("UNMUTE OUT: NID=0x%x\n", node->nid);
+       val = (node->amp_out_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+       ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+       if (val >= ofs)
+               val -= ofs;
+       val |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
+       val |= AC_AMP_SET_OUTPUT;
+       return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val);
+}
+
+/*
+ * unmute (and set max vol) the input amplifier
+ */
+static int unmute_input(struct hda_codec *codec, struct hda_gnode *node, unsigned int index)
+{
+       unsigned int val, ofs;
+       snd_printdd("UNMUTE IN: NID=0x%x IDX=0x%x\n", node->nid, index);
+       val = (node->amp_in_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+       ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+       if (val >= ofs)
+               val -= ofs;
+       val |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT;
+       val |= AC_AMP_SET_INPUT;
+       // awk added - fixed to allow unmuting of indexed amps
+       val |= index << AC_AMP_SET_INDEX_SHIFT;
+       return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val);
+}
+
+/*
+ * select the input connection of the given node.
+ */
+static int select_input_connection(struct hda_codec *codec, struct hda_gnode *node,
+                                  unsigned int index)
+{
+       snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index);
+       return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_CONNECT_SEL, index);
+}
+
+/*
+ * clear checked flag of each node in the node list
+ */
+static void clear_check_flags(struct hda_gspec *spec)
+{
+       struct list_head *p;
+       struct hda_gnode *node;
+
+       list_for_each(p, &spec->nid_list) {
+               node = list_entry(p, struct hda_gnode, list);
+               node->checked = 0;
+       }
+}
+
+/*
+ * parse the output path recursively until reach to an audio output widget
+ *
+ * returns 0 if not found, 1 if found, or a negative error code.
+ */
+static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec,
+                            struct hda_gnode *node)
+{
+       int i, err;
+       struct hda_gnode *child;
+
+       if (node->checked)
+               return 0;
+
+       node->checked = 1;
+       if (node->type == AC_WID_AUD_OUT) {
+               if (node->wid_caps & AC_WCAP_DIGITAL) {
+                       snd_printdd("Skip Digital OUT node %x\n", node->nid);
+                       return 0;
+               }
+               snd_printdd("AUD_OUT found %x\n", node->nid);
+               if (spec->dac_node) {
+                       /* already DAC node is assigned, just unmute & connect */
+                       return node == spec->dac_node;
+               }
+               spec->dac_node = node;
+               if (node->wid_caps & AC_WCAP_OUT_AMP) {
+                       spec->pcm_vol_node = node;
+                       spec->pcm_vol_index = 0;
+               }
+               return 1; /* found */
+       }
+
+       for (i = 0; i < node->nconns; i++) {
+               child = hda_get_node(spec, node->conn_list[i]);
+               if (! child)
+                       continue;
+               err = parse_output_path(codec, spec, child);
+               if (err < 0)
+                       return err;
+               else if (err > 0) {
+                       /* found one,
+                        * select the path, unmute both input and output
+                        */
+                       if (node->nconns > 1)
+                               select_input_connection(codec, node, i);
+                       unmute_input(codec, node, i);
+                       unmute_output(codec, node);
+                       if (! spec->pcm_vol_node) {
+                               if (node->wid_caps & AC_WCAP_IN_AMP) {
+                                       spec->pcm_vol_node = node;
+                                       spec->pcm_vol_index = i;
+                               } else if (node->wid_caps & AC_WCAP_OUT_AMP) {
+                                       spec->pcm_vol_node = node;
+                                       spec->pcm_vol_index = 0;
+                               }
+                       }
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/*
+ * Look for the output PIN widget with the given jack type
+ * and parse the output path to that PIN.
+ *
+ * Returns the PIN node when the path to DAC is established.
+ */
+static struct hda_gnode *parse_output_jack(struct hda_codec *codec,
+                                          struct hda_gspec *spec,
+                                          int jack_type)
+{
+       struct list_head *p;
+       struct hda_gnode *node;
+       int err;
+
+       list_for_each(p, &spec->nid_list) {
+               node = list_entry(p, struct hda_gnode, list);
+               if (node->type != AC_WID_PIN)
+                       continue;
+               /* output capable? */
+               if (! (node->pin_caps & AC_PINCAP_OUT))
+                       continue;
+               if (jack_type >= 0) {
+                       if (jack_type != get_defcfg_type(node))
+                               continue;
+                       if (node->wid_caps & AC_WCAP_DIGITAL)
+                               continue; /* skip SPDIF */
+               } else {
+                       /* output as default? */
+                       if (! (node->pin_ctl & AC_PINCTL_OUT_EN))
+                               continue;
+               }
+               clear_check_flags(spec);
+               err = parse_output_path(codec, spec, node);
+               if (err < 0)
+                       return NULL;
+               else if (err > 0) {
+                       /* unmute the PIN output */
+                       unmute_output(codec, node);
+                       /* set PIN-Out enable */
+                       snd_hda_codec_write(codec, node->nid, 0,
+                                           AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                           AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+                       return node;
+               }
+       }
+       return NULL;
+}
+
+
+/*
+ * parse outputs
+ */
+static int parse_output(struct hda_codec *codec)
+{
+       struct hda_gspec *spec = codec->spec;
+       struct hda_gnode *node;
+
+       /*
+        * Look for the output PIN widget
+        */
+       /* first, look for the line-out pin */
+       node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT);
+       if (node) /* found, remember the PIN node */
+               spec->out_pin_node = node;
+       /* look for the HP-out pin */
+       node = parse_output_jack(codec, spec, AC_JACK_HP_OUT);
+       if (node) {
+               if (! spec->out_pin_node)
+                       spec->out_pin_node = node;
+       }
+
+       if (! spec->out_pin_node) {
+               /* no line-out or HP pins found,
+                * then choose for the first output pin
+                */
+               spec->out_pin_node = parse_output_jack(codec, spec, -1);
+               if (! spec->out_pin_node)
+                       snd_printd("hda_generic: no proper output path found\n");
+       }
+
+       return 0;
+}
+
+/*
+ * input MUX
+ */
+
+/* control callbacks */
+static int capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct hda_gspec *spec = codec->spec;
+       return snd_hda_input_mux_info(&spec->input_mux, uinfo);
+}
+
+static int capture_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct hda_gspec *spec = codec->spec;
+
+       ucontrol->value.enumerated.item[0] = spec->cur_cap_src;
+       return 0;
+}
+
+static int capture_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct hda_gspec *spec = codec->spec;
+       return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol,
+                                    spec->adc_node->nid, &spec->cur_cap_src);
+}
+
+/*
+ * return the string name of the given input PIN widget
+ */
+static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
+{
+       unsigned int location = get_defcfg_location(node);
+       switch (get_defcfg_type(node)) {
+       case AC_JACK_LINE_IN:
+               if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+                       return "Front Line";
+               return "Line";
+       case AC_JACK_CD:
+               if (pinctl)
+                       *pinctl |= AC_PIN_VREF_GRD;
+               return "CD";
+       case AC_JACK_AUX:
+               if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+                       return "Front Aux";
+               return "Aux";
+       case AC_JACK_MIC_IN:
+               if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+                       return "Front Mic";
+               return "Mic";
+       case AC_JACK_SPDIF_IN:
+               return "SPDIF";
+       case AC_JACK_DIG_OTHER_IN:
+               return "Digital";
+       }
+       return NULL;
+}
+
+/*
+ * parse the nodes recursively until reach to the input PIN
+ *
+ * returns 0 if not found, 1 if found, or a negative error code.
+ */
+static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
+                              struct hda_gnode *node)
+{
+       int i, err;
+       unsigned int pinctl;
+       char *label;
+       const char *type;
+
+       if (node->checked)
+               return 0;
+
+       node->checked = 1;
+       if (node->type != AC_WID_PIN) {
+               for (i = 0; i < node->nconns; i++) {
+                       struct hda_gnode *child;
+                       child = hda_get_node(spec, node->conn_list[i]);
+                       if (! child)
+                               continue;
+                       err = parse_adc_sub_nodes(codec, spec, child);
+                       if (err < 0)
+                               return err;
+                       if (err > 0) {
+                               /* found one,
+                                * select the path, unmute both input and output
+                                */
+                               if (node->nconns > 1)
+                                       select_input_connection(codec, node, i);
+                               unmute_input(codec, node, i);
+                               unmute_output(codec, node);
+                               return err;
+                       }
+               }
+               return 0;
+       }
+
+       /* input capable? */
+       if (! (node->pin_caps & AC_PINCAP_IN))
+               return 0;
+
+       if (node->wid_caps & AC_WCAP_DIGITAL)
+               return 0; /* skip SPDIF */
+
+       if (spec->input_mux.num_items >= HDA_MAX_NUM_INPUTS) {
+               snd_printk(KERN_ERR "hda_generic: Too many items for capture\n");
+               return -EINVAL;
+       }
+
+       pinctl = AC_PINCTL_IN_EN;
+       /* create a proper capture source label */
+       type = get_input_type(node, &pinctl);
+       if (! type) {
+               /* input as default? */
+               if (! (node->pin_ctl & AC_PINCTL_IN_EN))
+                       return 0;
+               type = "Input";
+       }
+       label = spec->cap_labels[spec->input_mux.num_items];
+       strcpy(label, type);
+       spec->input_mux.items[spec->input_mux.num_items].label = label;
+
+       /* unmute the PIN external input */
+       unmute_input(codec, node, 0); /* index = 0? */
+       /* set PIN-In enable */
+       snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+
+       return 1; /* found */
+}
+
+/*
+ * parse input
+ */
+static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
+{
+       struct hda_gspec *spec = codec->spec;
+       struct hda_gnode *node;
+       int i, err;
+
+       snd_printdd("AUD_IN = %x\n", adc_node->nid);
+       clear_check_flags(spec);
+
+       // awk added - fixed no recording due to muted widget
+       unmute_input(codec, adc_node, 0);
+       
+       /*
+        * check each connection of the ADC
+        * if it reaches to a proper input PIN, add the path as the
+        * input path.
+        */
+       for (i = 0; i < adc_node->nconns; i++) {
+               node = hda_get_node(spec, adc_node->conn_list[i]);
+               if (! node)
+                       continue;
+               err = parse_adc_sub_nodes(codec, spec, node);
+               if (err < 0)
+                       return err;
+               else if (err > 0) {
+                       struct hda_input_mux_item *csrc = &spec->input_mux.items[spec->input_mux.num_items];
+                       char *buf = spec->cap_labels[spec->input_mux.num_items];
+                       int ocap;
+                       for (ocap = 0; ocap < spec->input_mux.num_items; ocap++) {
+                               if (! strcmp(buf, spec->cap_labels[ocap])) {
+                                       /* same label already exists,
+                                        * put the index number to be unique
+                                        */
+                                       sprintf(buf, "%s %d", spec->cap_labels[ocap],
+                                               spec->input_mux.num_items);
+                               }
+                       }
+                       csrc->index = i;
+                       spec->input_mux.num_items++;
+               }
+       }
+
+       if (! spec->input_mux.num_items)
+               return 0; /* no input path found... */
+
+       snd_printdd("[Capture Source] NID=0x%x, #SRC=%d\n", adc_node->nid, spec->input_mux.num_items);
+       for (i = 0; i < spec->input_mux.num_items; i++)
+               snd_printdd("  [%s] IDX=0x%x\n", spec->input_mux.items[i].label,
+                           spec->input_mux.items[i].index);
+
+       spec->adc_node = adc_node;
+       return 1;
+}
+
+/*
+ * parse input
+ */
+static int parse_input(struct hda_codec *codec)
+{
+       struct hda_gspec *spec = codec->spec;
+       struct list_head *p;
+       struct hda_gnode *node;
+       int err;
+
+       /*
+        * At first we look for an audio input widget.
+        * If it reaches to certain input PINs, we take it as the
+        * input path.
+        */
+       list_for_each(p, &spec->nid_list) {
+               node = list_entry(p, struct hda_gnode, list);
+               if (node->wid_caps & AC_WCAP_DIGITAL)
+                       continue; /* skip SPDIF */
+               if (node->type == AC_WID_AUD_IN) {
+                       err = parse_input_path(codec, node);
+                       if (err < 0)
+                               return err;
+                       else if (err > 0)
+                               return 0;
+               }
+       }
+       snd_printd("hda_generic: no proper input path found\n");
+       return 0;
+}
+
+/*
+ * create mixer controls if possible
+ */
+#define DIR_OUT                0x1
+#define DIR_IN         0x2
+
+static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
+                       unsigned int index, const char *type, const char *dir_sfx)
+{
+       char name[32];
+       int err;
+       int created = 0;
+       snd_kcontrol_new_t knew;
+
+       if (type)
+               sprintf(name, "%s %s Switch", type, dir_sfx);
+       else
+               sprintf(name, "%s Switch", dir_sfx);
+       if ((node->wid_caps & AC_WCAP_IN_AMP) &&
+           (node->amp_in_caps & AC_AMPCAP_MUTE)) {
+               knew = (snd_kcontrol_new_t)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT);
+               snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
+               if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+                       return err;
+               created = 1;
+       } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
+                  (node->amp_out_caps & AC_AMPCAP_MUTE)) {
+               knew = (snd_kcontrol_new_t)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT);
+               snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
+               if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+                       return err;
+               created = 1;
+       }
+
+       if (type)
+               sprintf(name, "%s %s Volume", type, dir_sfx);
+       else
+               sprintf(name, "%s Volume", dir_sfx);
+       if ((node->wid_caps & AC_WCAP_IN_AMP) &&
+           (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
+               knew = (snd_kcontrol_new_t)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
+               snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
+               if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+                       return err;
+               created = 1;
+       } else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
+                  (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
+               knew = (snd_kcontrol_new_t)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
+               snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
+               if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+                       return err;
+               created = 1;
+       }
+
+       return created;
+}
+
+/*
+ * check whether the controls with the given name and direction suffix already exist
+ */
+static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir)
+{
+       snd_ctl_elem_id_t id;
+       memset(&id, 0, sizeof(id));
+       sprintf(id.name, "%s %s Volume", type, dir);
+       id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       if (snd_ctl_find_id(codec->bus->card, &id))
+               return 1;
+       sprintf(id.name, "%s %s Switch", type, dir);
+       id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       if (snd_ctl_find_id(codec->bus->card, &id))
+               return 1;
+       return 0;
+}
+
+/*
+ * build output mixer controls
+ */
+static int build_output_controls(struct hda_codec *codec)
+{
+       struct hda_gspec *spec = codec->spec;
+       int err;
+
+       err = create_mixer(codec, spec->pcm_vol_node, spec->pcm_vol_index,
+                          "PCM", "Playback");
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+/* create capture volume/switch */
+static int build_input_controls(struct hda_codec *codec)
+{
+       struct hda_gspec *spec = codec->spec;
+       struct hda_gnode *adc_node = spec->adc_node;
+       int err;
+
+       if (! adc_node)
+               return 0; /* not found */
+
+       /* create capture volume and switch controls if the ADC has an amp */
+       err = create_mixer(codec, adc_node, 0, NULL, "Capture");
+
+       /* create input MUX if multiple sources are available */
+       if (spec->input_mux.num_items > 1) {
+               static snd_kcontrol_new_t cap_sel = {
+                       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+                       .name = "Capture Source",
+                       .info = capture_source_info,
+                       .get = capture_source_get,
+                       .put = capture_source_put,
+               };
+               if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&cap_sel, codec))) < 0)
+                       return err;
+               spec->cur_cap_src = 0;
+               select_input_connection(codec, adc_node, spec->input_mux.items[0].index);
+       }
+       return 0;
+}
+
+
+/*
+ * parse the nodes recursively until reach to the output PIN.
+ *
+ * returns 0 - if not found,
+ *         1 - if found, but no mixer is created
+ *         2 - if found and mixer was already created, (just skip)
+ *         a negative error code
+ */
+static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec,
+                              struct hda_gnode *node, struct hda_gnode *dest_node,
+                              const char *type)
+{
+       int i, err;
+
+       if (node->checked)
+               return 0;
+
+       node->checked = 1;
+       if (node == dest_node) {
+               /* loopback connection found */
+               return 1;
+       }
+
+       for (i = 0; i < node->nconns; i++) {
+               struct hda_gnode *child = hda_get_node(spec, node->conn_list[i]);
+               if (! child)
+                       continue;
+               err = parse_loopback_path(codec, spec, child, dest_node, type);
+               if (err < 0)
+                       return err;
+               else if (err >= 1) {
+                       if (err == 1) {
+                               err = create_mixer(codec, node, i, type, "Playback");
+                               if (err < 0)
+                                       return err;
+                               if (err > 0)
+                                       return 2; /* ok, created */
+                               /* not created, maybe in the lower path */
+                               err = 1;
+                       }
+                       /* connect and unmute */
+                       if (node->nconns > 1)
+                               select_input_connection(codec, node, i);
+                       unmute_input(codec, node, i);
+                       unmute_output(codec, node);
+                       return err;
+               }
+       }
+       return 0;
+}
+
+/*
+ * parse the tree and build the loopback controls
+ */
+static int build_loopback_controls(struct hda_codec *codec)
+{
+       struct hda_gspec *spec = codec->spec;
+       struct list_head *p;
+       struct hda_gnode *node;
+       int err;
+       const char *type;
+
+       if (! spec->out_pin_node)
+               return 0;
+
+       list_for_each(p, &spec->nid_list) {
+               node = list_entry(p, struct hda_gnode, list);
+               if (node->type != AC_WID_PIN)
+                       continue;
+               /* input capable? */
+               if (! (node->pin_caps & AC_PINCAP_IN))
+                       return 0;
+               type = get_input_type(node, NULL);
+               if (type) {
+                       if (check_existing_control(codec, type, "Playback"))
+                               continue;
+                       clear_check_flags(spec);
+                       err = parse_loopback_path(codec, spec, spec->out_pin_node,
+                                                 node, type);
+                       if (err < 0)
+                               return err;
+                       if (! err)
+                               continue;
+               }
+       }
+       return 0;
+}
+
+/*
+ * build mixer controls
+ */
+static int build_generic_controls(struct hda_codec *codec)
+{
+       int err;
+
+       if ((err = build_input_controls(codec)) < 0 ||
+           (err = build_output_controls(codec)) < 0 ||
+           (err = build_loopback_controls(codec)) < 0)
+               return err;
+
+       return 0;
+}
+
+/*
+ * PCM
+ */
+static struct hda_pcm_stream generic_pcm_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+};
+
+static int build_generic_pcms(struct hda_codec *codec)
+{
+       struct hda_gspec *spec = codec->spec;
+       struct hda_pcm *info = &spec->pcm_rec;
+
+       if (! spec->dac_node && ! spec->adc_node) {
+               snd_printd("hda_generic: no PCM found\n");
+               return 0;
+       }
+
+       codec->num_pcms = 1;
+       codec->pcm_info = info;
+
+       info->name = "HDA Generic";
+       if (spec->dac_node) {
+               info->stream[0] = generic_pcm_playback;
+               info->stream[0].nid = spec->dac_node->nid;
+       }
+       if (spec->adc_node) {
+               info->stream[1] = generic_pcm_playback;
+               info->stream[1].nid = spec->adc_node->nid;
+       }
+
+       return 0;
+}
+
+
+/*
+ */
+static struct hda_codec_ops generic_patch_ops = {
+       .build_controls = build_generic_controls,
+       .build_pcms = build_generic_pcms,
+       .free = snd_hda_generic_free,
+};
+
+/*
+ * the generic parser
+ */
+int snd_hda_parse_generic_codec(struct hda_codec *codec)
+{
+       struct hda_gspec *spec;
+       int err;
+
+       spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL) {
+               printk(KERN_ERR "hda_generic: can't allocate spec\n");
+               return -ENOMEM;
+       }
+       codec->spec = spec;
+       INIT_LIST_HEAD(&spec->nid_list);
+
+       if ((err = build_afg_tree(codec)) < 0)
+               goto error;
+
+       if ((err = parse_input(codec)) < 0 ||
+           (err = parse_output(codec)) < 0)
+               goto error;
+
+       codec->patch_ops = generic_patch_ops;
+
+       return 0;
+
+ error:
+       snd_hda_generic_free(codec);
+       return err;
+}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
new file mode 100644 (file)
index 0000000..959953c
--- /dev/null
@@ -0,0 +1,1451 @@
+/*
+ *
+ *  hda_intel.c - Implementation of primary alsa driver code base for Intel HD Audio.
+ *
+ *  Copyright(c) 2004 Intel Corporation. All rights reserved.
+ *
+ *  Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *                     PeiSen Hou <pshou@realtek.com.tw>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the 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.
+ *
+ *  CONTACTS:
+ *
+ *  Matt Jared         matt.jared@intel.com
+ *  Andy Kopp          andy.kopp@intel.com
+ *  Dan Kogan          dan.d.kogan@intel.com
+ *
+ *  CHANGES:
+ *
+ *  2004.12.01 Major rewrite by tiwai, merged the work of pshou
+ * 
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include "hda_codec.h"
+
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static char *model[SNDRV_CARDS];
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Intel HD audio interface.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
+module_param_array(model, charp, NULL, 0444);
+MODULE_PARM_DESC(model, "Use the given board model.");
+
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
+                        "{Intel, ICH6M},"
+                        "{Intel, ICH7},"
+                        "{Intel, ESB2}}");
+MODULE_DESCRIPTION("Intel HDA driver");
+
+#define SFX    "hda-intel: "
+
+/*
+ * registers
+ */
+#define ICH6_REG_GCAP                  0x00
+#define ICH6_REG_VMIN                  0x02
+#define ICH6_REG_VMAJ                  0x03
+#define ICH6_REG_OUTPAY                        0x04
+#define ICH6_REG_INPAY                 0x06
+#define ICH6_REG_GCTL                  0x08
+#define ICH6_REG_WAKEEN                        0x0c
+#define ICH6_REG_STATESTS              0x0e
+#define ICH6_REG_GSTS                  0x10
+#define ICH6_REG_INTCTL                        0x20
+#define ICH6_REG_INTSTS                        0x24
+#define ICH6_REG_WALCLK                        0x30
+#define ICH6_REG_SYNC                  0x34    
+#define ICH6_REG_CORBLBASE             0x40
+#define ICH6_REG_CORBUBASE             0x44
+#define ICH6_REG_CORBWP                        0x48
+#define ICH6_REG_CORBRP                        0x4A
+#define ICH6_REG_CORBCTL               0x4c
+#define ICH6_REG_CORBSTS               0x4d
+#define ICH6_REG_CORBSIZE              0x4e
+
+#define ICH6_REG_RIRBLBASE             0x50
+#define ICH6_REG_RIRBUBASE             0x54
+#define ICH6_REG_RIRBWP                        0x58
+#define ICH6_REG_RINTCNT               0x5a
+#define ICH6_REG_RIRBCTL               0x5c
+#define ICH6_REG_RIRBSTS               0x5d
+#define ICH6_REG_RIRBSIZE              0x5e
+
+#define ICH6_REG_IC                    0x60
+#define ICH6_REG_IR                    0x64
+#define ICH6_REG_IRS                   0x68
+#define   ICH6_IRS_VALID       (1<<1)
+#define   ICH6_IRS_BUSY                (1<<0)
+
+#define ICH6_REG_DPLBASE               0x70
+#define ICH6_REG_DPUBASE               0x74
+#define   ICH6_DPLBASE_ENABLE  0x1     /* Enable position buffer */
+
+/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
+enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
+
+/* stream register offsets from stream base */
+#define ICH6_REG_SD_CTL                        0x00
+#define ICH6_REG_SD_STS                        0x03
+#define ICH6_REG_SD_LPIB               0x04
+#define ICH6_REG_SD_CBL                        0x08
+#define ICH6_REG_SD_LVI                        0x0c
+#define ICH6_REG_SD_FIFOW              0x0e
+#define ICH6_REG_SD_FIFOSIZE           0x10
+#define ICH6_REG_SD_FORMAT             0x12
+#define ICH6_REG_SD_BDLPL              0x18
+#define ICH6_REG_SD_BDLPU              0x1c
+
+/* PCI space */
+#define ICH6_PCIREG_TCSEL      0x44
+
+/*
+ * other constants
+ */
+
+/* max number of SDs */
+#define MAX_ICH6_DEV           8
+/* max number of fragments - we may use more if allocating more pages for BDL */
+#define AZX_MAX_FRAG           (PAGE_SIZE / (MAX_ICH6_DEV * 16))
+/* max buffer size - no h/w limit, you can increase as you like */
+#define AZX_MAX_BUF_SIZE       (1024*1024*1024)
+/* max number of PCM devics per card */
+#define AZX_MAX_PCMS           8
+
+/* RIRB int mask: overrun[2], response[0] */
+#define RIRB_INT_RESPONSE      0x01
+#define RIRB_INT_OVERRUN       0x04
+#define RIRB_INT_MASK          0x05
+
+/* STATESTS int mask: SD2,SD1,SD0 */
+#define STATESTS_INT_MASK      0x07
+#define AZX_MAX_CODECS         3
+
+/* SD_CTL bits */
+#define SD_CTL_STREAM_RESET    0x01    /* stream reset bit */
+#define SD_CTL_DMA_START       0x02    /* stream DMA start bit */
+#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
+#define SD_CTL_STREAM_TAG_SHIFT        20
+
+/* SD_CTL and SD_STS */
+#define SD_INT_DESC_ERR                0x10    /* descriptor error interrupt */
+#define SD_INT_FIFO_ERR                0x08    /* FIFO error interrupt */
+#define SD_INT_COMPLETE                0x04    /* completion interrupt */
+#define SD_INT_MASK            (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|SD_INT_COMPLETE)
+
+/* SD_STS */
+#define SD_STS_FIFO_READY      0x20    /* FIFO ready */
+
+/* INTCTL and INTSTS */
+#define ICH6_INT_ALL_STREAM    0xff            /* all stream interrupts */
+#define ICH6_INT_CTRL_EN       0x40000000      /* controller interrupt enable bit */
+#define ICH6_INT_GLOBAL_EN     0x80000000      /* global interrupt enable bit */
+
+/* GCTL reset bit */
+#define ICH6_GCTL_RESET                (1<<0)
+
+/* CORB/RIRB control, read/write pointer */
+#define ICH6_RBCTL_DMA_EN      0x02    /* enable DMA */
+#define ICH6_RBCTL_IRQ_EN      0x01    /* enable IRQ */
+#define ICH6_RBRWP_CLR         0x8000  /* read/write pointer clear */
+/* below are so far hardcoded - should read registers in future */
+#define ICH6_MAX_CORB_ENTRIES  256
+#define ICH6_MAX_RIRB_ENTRIES  256
+
+
+/*
+ * Use CORB/RIRB for communication from/to codecs.
+ * This is the way recommended by Intel (see below).
+ */
+#define USE_CORB_RIRB
+
+/*
+ * Define this if use the position buffer instead of reading SD_LPIB
+ * It's not used as default since SD_LPIB seems to give more accurate position
+ */
+/* #define USE_POSBUF */
+
+/*
+ */
+
+typedef struct snd_azx azx_t;
+typedef struct snd_azx_rb azx_rb_t;
+typedef struct snd_azx_dev azx_dev_t;
+
+struct snd_azx_dev {
+       u32 *bdl;                       /* virtual address of the BDL */
+       dma_addr_t bdl_addr;            /* physical address of the BDL */
+       volatile u32 *posbuf;                   /* position buffer pointer */
+
+       unsigned int bufsize;           /* size of the play buffer in bytes */
+       unsigned int fragsize;          /* size of each period in bytes */
+       unsigned int frags;             /* number for period in the play buffer */
+       unsigned int fifo_size;         /* FIFO size */
+
+       void __iomem *sd_addr;          /* stream descriptor pointer */
+
+       u32 sd_int_sta_mask;            /* stream int status mask */
+
+       /* pcm support */
+       snd_pcm_substream_t *substream; /* assigned substream, set in PCM open */
+       unsigned int format_val;        /* format value to be set in the controller and the codec */
+       unsigned char stream_tag;       /* assigned stream */
+       unsigned char index;            /* stream index */
+
+       unsigned int opened: 1;
+       unsigned int running: 1;
+};
+
+/* CORB/RIRB */
+struct snd_azx_rb {
+       u32 *buf;               /* CORB/RIRB buffer
+                                * Each CORB entry is 4byte, RIRB is 8byte
+                                */
+       dma_addr_t addr;        /* physical address of CORB/RIRB buffer */
+       /* for RIRB */
+       unsigned short rp, wp;  /* read/write pointers */
+       int cmds;               /* number of pending requests */
+       u32 res;                /* last read value */
+};
+
+struct snd_azx {
+       snd_card_t *card;
+       struct pci_dev *pci;
+
+       /* pci resources */
+       unsigned long addr;
+       void __iomem *remap_addr;
+       int irq;
+
+       /* locks */
+       spinlock_t reg_lock;
+       struct semaphore open_mutex;
+
+       /* streams */
+       azx_dev_t azx_dev[MAX_ICH6_DEV];
+
+       /* PCM */
+       unsigned int pcm_devs;
+       snd_pcm_t *pcm[AZX_MAX_PCMS];
+
+       /* HD codec */
+       unsigned short codec_mask;
+       struct hda_bus *bus;
+
+       /* CORB/RIRB */
+       azx_rb_t corb;
+       azx_rb_t rirb;
+
+       /* BDL, CORB/RIRB and position buffers */
+       struct snd_dma_buffer bdl;
+       struct snd_dma_buffer rb;
+       struct snd_dma_buffer posbuf;
+};
+
+/*
+ * macros for easy use
+ */
+#define azx_writel(chip,reg,value) \
+       writel(value, (chip)->remap_addr + ICH6_REG_##reg)
+#define azx_readl(chip,reg) \
+       readl((chip)->remap_addr + ICH6_REG_##reg)
+#define azx_writew(chip,reg,value) \
+       writew(value, (chip)->remap_addr + ICH6_REG_##reg)
+#define azx_readw(chip,reg) \
+       readw((chip)->remap_addr + ICH6_REG_##reg)
+#define azx_writeb(chip,reg,value) \
+       writeb(value, (chip)->remap_addr + ICH6_REG_##reg)
+#define azx_readb(chip,reg) \
+       readb((chip)->remap_addr + ICH6_REG_##reg)
+
+#define azx_sd_writel(dev,reg,value) \
+       writel(value, (dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_readl(dev,reg) \
+       readl((dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_writew(dev,reg,value) \
+       writew(value, (dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_readw(dev,reg) \
+       readw((dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_writeb(dev,reg,value) \
+       writeb(value, (dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_readb(dev,reg) \
+       readb((dev)->sd_addr + ICH6_REG_##reg)
+
+/* for pcm support */
+#define get_azx_dev(substream) (azx_dev_t*)(substream->runtime->private_data)
+
+/* Get the upper 32bit of the given dma_addr_t
+ * Compiler should optimize and eliminate the code if dma_addr_t is 32bit
+ */
+#define upper_32bit(addr) (sizeof(addr) > 4 ? (u32)((addr) >> 32) : (u32)0)
+
+
+/*
+ * Interface for HD codec
+ */
+
+#ifdef USE_CORB_RIRB
+/*
+ * CORB / RIRB interface
+ */
+static int azx_alloc_cmd_io(azx_t *chip)
+{
+       int err;
+
+       /* single page (at least 4096 bytes) must suffice for both ringbuffes */
+       err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+                                 PAGE_SIZE, &chip->rb);
+       if (err < 0) {
+               snd_printk(KERN_ERR SFX "cannot allocate CORB/RIRB\n");
+               return err;
+       }
+       return 0;
+}
+
+static void azx_init_cmd_io(azx_t *chip)
+{
+       /* CORB set up */
+       chip->corb.addr = chip->rb.addr;
+       chip->corb.buf = (u32 *)chip->rb.area;
+       azx_writel(chip, CORBLBASE, (u32)chip->corb.addr);
+       azx_writel(chip, CORBUBASE, upper_32bit(chip->corb.addr));
+
+       /* set the corb write pointer to 0 */
+       azx_writew(chip, CORBWP, 0);
+       /* reset the corb hw read pointer */
+       azx_writew(chip, CORBRP, ICH6_RBRWP_CLR);
+       /* enable corb dma */
+       azx_writeb(chip, CORBCTL, ICH6_RBCTL_DMA_EN);
+
+       /* RIRB set up */
+       chip->rirb.addr = chip->rb.addr + 2048;
+       chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
+       azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
+       azx_writel(chip, RIRBUBASE, upper_32bit(chip->rirb.addr));
+
+       /* reset the rirb hw write pointer */
+       azx_writew(chip, RIRBWP, ICH6_RBRWP_CLR);
+       /* set N=1, get RIRB response interrupt for new entry */
+       azx_writew(chip, RINTCNT, 1);
+       /* enable rirb dma and response irq */
+#ifdef USE_CORB_RIRB
+       azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
+#else
+       azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN);
+#endif
+       chip->rirb.rp = chip->rirb.cmds = 0;
+}
+
+static void azx_free_cmd_io(azx_t *chip)
+{
+       /* disable ringbuffer DMAs */
+       azx_writeb(chip, RIRBCTL, 0);
+       azx_writeb(chip, CORBCTL, 0);
+}
+
+/* send a command */
+static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
+                       unsigned int verb, unsigned int para)
+{
+       azx_t *chip = codec->bus->private_data;
+       unsigned int wp;
+       u32 val;
+
+       val = (u32)(codec->addr & 0x0f) << 28;
+       val |= (u32)direct << 27;
+       val |= (u32)nid << 20;
+       val |= verb << 8;
+       val |= para;
+
+       /* add command to corb */
+       wp = azx_readb(chip, CORBWP);
+       wp++;
+       wp %= ICH6_MAX_CORB_ENTRIES;
+
+       spin_lock_irq(&chip->reg_lock);
+       chip->rirb.cmds++;
+       chip->corb.buf[wp] = cpu_to_le32(val);
+       azx_writel(chip, CORBWP, wp);
+       spin_unlock_irq(&chip->reg_lock);
+
+       return 0;
+}
+
+#define ICH6_RIRB_EX_UNSOL_EV  (1<<4)
+
+/* retrieve RIRB entry - called from interrupt handler */
+static void azx_update_rirb(azx_t *chip)
+{
+       unsigned int rp, wp;
+       u32 res, res_ex;
+
+       wp = azx_readb(chip, RIRBWP);
+       if (wp == chip->rirb.wp)
+               return;
+       chip->rirb.wp = wp;
+               
+       while (chip->rirb.rp != wp) {
+               chip->rirb.rp++;
+               chip->rirb.rp %= ICH6_MAX_RIRB_ENTRIES;
+
+               rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
+               res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
+               res = le32_to_cpu(chip->rirb.buf[rp]);
+               if (res_ex & ICH6_RIRB_EX_UNSOL_EV)
+                       snd_hda_queue_unsol_event(chip->bus, res, res_ex);
+               else if (chip->rirb.cmds) {
+                       chip->rirb.cmds--;
+                       chip->rirb.res = res;
+               }
+       }
+}
+
+/* receive a response */
+static unsigned int azx_get_response(struct hda_codec *codec)
+{
+       azx_t *chip = codec->bus->private_data;
+       int timeout = 50;
+
+       while (chip->rirb.cmds) {
+               if (! --timeout) {
+                       snd_printk(KERN_ERR "azx_get_response timeout\n");
+                       chip->rirb.rp = azx_readb(chip, RIRBWP);
+                       chip->rirb.cmds = 0;
+                       return -1;
+               }
+               msleep(1);
+       }
+       return chip->rirb.res; /* the last value */
+}
+
+#else
+/*
+ * Use the single immediate command instead of CORB/RIRB for simplicity
+ *
+ * Note: according to Intel, this is not preferred use.  The command was
+ *       intended for the BIOS only, and may get confused with unsolicited
+ *       responses.  So, we shouldn't use it for normal operation from the
+ *       driver.
+ *       I left the codes, however, for debugging/testing purposes.
+ */
+
+#define azx_alloc_cmd_io(chip) 0
+#define azx_init_cmd_io(chip)
+#define azx_free_cmd_io(chip)
+
+/* send a command */
+static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
+                       unsigned int verb, unsigned int para)
+{
+       azx_t *chip = codec->bus->private_data;
+       u32 val;
+       int timeout = 50;
+
+       val = (u32)(codec->addr & 0x0f) << 28;
+       val |= (u32)direct << 27;
+       val |= (u32)nid << 20;
+       val |= verb << 8;
+       val |= para;
+
+       while (timeout--) {
+               /* check ICB busy bit */
+               if (! (azx_readw(chip, IRS) & ICH6_IRS_BUSY)) {
+                       /* Clear IRV valid bit */
+                       azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_VALID);
+                       azx_writel(chip, IC, val);
+                       azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_BUSY);
+                       return 0;
+               }
+               udelay(1);
+       }
+       snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", azx_readw(chip, IRS), val);
+       return -EIO;
+}
+
+/* receive a response */
+static unsigned int azx_get_response(struct hda_codec *codec)
+{
+       azx_t *chip = codec->bus->private_data;
+       int timeout = 50;
+
+       while (timeout--) {
+               /* check IRV busy bit */
+               if (azx_readw(chip, IRS) & ICH6_IRS_VALID)
+                       return azx_readl(chip, IR);
+               udelay(1);
+       }
+       snd_printd(SFX "get_response timeout: IRS=0x%x\n", azx_readw(chip, IRS));
+       return (unsigned int)-1;
+}
+
+#define azx_update_rirb(chip)
+
+#endif /* USE_CORB_RIRB */
+
+/* reset codec link */
+static int azx_reset(azx_t *chip)
+{
+       int count;
+
+       /* reset controller */
+       azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~ICH6_GCTL_RESET);
+
+       count = 50;
+       while (azx_readb(chip, GCTL) && --count)
+               msleep(1);
+
+       /* delay for >= 100us for codec PLL to settle per spec
+        * Rev 0.9 section 5.5.1
+        */
+       msleep(1);
+
+       /* Bring controller out of reset */
+       azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | ICH6_GCTL_RESET);
+
+       count = 50;
+       while (! azx_readb(chip, GCTL) && --count)
+               msleep(1);
+
+       /* Brent Chartrand said to wait >= 540us for codecs to intialize */
+       msleep(1);
+
+       /* check to see if controller is ready */
+       if (! azx_readb(chip, GCTL)) {
+               snd_printd("azx_reset: controller not ready!\n");
+               return -EBUSY;
+       }
+
+       /* detect codecs */
+       if (! chip->codec_mask) {
+               chip->codec_mask = azx_readw(chip, STATESTS);
+               snd_printdd("codec_mask = 0x%x\n", chip->codec_mask);
+       }
+
+       return 0;
+}
+
+
+/*
+ * Lowlevel interface
+ */  
+
+/* enable interrupts */
+static void azx_int_enable(azx_t *chip)
+{
+       /* enable controller CIE and GIE */
+       azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) |
+                  ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN);
+}
+
+/* disable interrupts */
+static void azx_int_disable(azx_t *chip)
+{
+       int i;
+
+       /* disable interrupts in stream descriptor */
+       for (i = 0; i < MAX_ICH6_DEV; i++) {
+               azx_dev_t *azx_dev = &chip->azx_dev[i];
+               azx_sd_writeb(azx_dev, SD_CTL,
+                             azx_sd_readb(azx_dev, SD_CTL) & ~SD_INT_MASK);
+       }
+
+       /* disable SIE for all streams */
+       azx_writeb(chip, INTCTL, 0);
+
+       /* disable controller CIE and GIE */
+       azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) &
+                  ~(ICH6_INT_CTRL_EN | ICH6_INT_GLOBAL_EN));
+}
+
+/* clear interrupts */
+static void azx_int_clear(azx_t *chip)
+{
+       int i;
+
+       /* clear stream status */
+       for (i = 0; i < MAX_ICH6_DEV; i++) {
+               azx_dev_t *azx_dev = &chip->azx_dev[i];
+               azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
+       }
+
+       /* clear STATESTS */
+       azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
+
+       /* clear rirb status */
+       azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
+
+       /* clear int status */
+       azx_writel(chip, INTSTS, ICH6_INT_CTRL_EN | ICH6_INT_ALL_STREAM);
+}
+
+/* start a stream */
+static void azx_stream_start(azx_t *chip, azx_dev_t *azx_dev)
+{
+       /* enable SIE */
+       azx_writeb(chip, INTCTL,
+                  azx_readb(chip, INTCTL) | (1 << azx_dev->index));
+       /* set DMA start and interrupt mask */
+       azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
+                     SD_CTL_DMA_START | SD_INT_MASK);
+}
+
+/* stop a stream */
+static void azx_stream_stop(azx_t *chip, azx_dev_t *azx_dev)
+{
+       /* stop DMA */
+       azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) &
+                     ~(SD_CTL_DMA_START | SD_INT_MASK));
+       azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
+       /* disable SIE */
+       azx_writeb(chip, INTCTL,
+                  azx_readb(chip, INTCTL) & ~(1 << azx_dev->index));
+}
+
+
+/*
+ * initialize the chip
+ */
+static void azx_init_chip(azx_t *chip)
+{
+       unsigned char tcsel_reg;
+
+       /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
+        * TCSEL == Traffic Class Select Register, which sets PCI express QOS
+        * Ensuring these bits are 0 clears playback static on some HD Audio codecs
+        */
+       pci_read_config_byte (chip->pci, ICH6_PCIREG_TCSEL, &tcsel_reg);
+       pci_write_config_byte(chip->pci, ICH6_PCIREG_TCSEL, tcsel_reg & 0xf8);
+
+       /* reset controller */
+       azx_reset(chip);
+
+       /* initialize interrupts */
+       azx_int_clear(chip);
+       azx_int_enable(chip);
+
+       /* initialize the codec command I/O */
+       azx_init_cmd_io(chip);
+
+#ifdef USE_POSBUF
+       /* program the position buffer */
+       azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
+       azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));
+#endif
+}
+
+
+/*
+ * interrupt handler
+ */
+static irqreturn_t azx_interrupt(int irq, void* dev_id, struct pt_regs *regs)
+{
+       azx_t *chip = dev_id;
+       azx_dev_t *azx_dev;
+       u32 status;
+       int i;
+
+       spin_lock(&chip->reg_lock);
+
+       status = azx_readl(chip, INTSTS);
+       if (status == 0) {
+               spin_unlock(&chip->reg_lock);
+               return IRQ_NONE;
+       }
+       
+       for (i = 0; i < MAX_ICH6_DEV; i++) {
+               azx_dev = &chip->azx_dev[i];
+               if (status & azx_dev->sd_int_sta_mask) {
+                       azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
+                       if (azx_dev->substream && azx_dev->running) {
+                               spin_unlock(&chip->reg_lock);
+                               snd_pcm_period_elapsed(azx_dev->substream);
+                               spin_lock(&chip->reg_lock);
+                       }
+               }
+       }
+
+       /* clear rirb int */
+       status = azx_readb(chip, RIRBSTS);
+       if (status & RIRB_INT_MASK) {
+               if (status & RIRB_INT_RESPONSE)
+                       azx_update_rirb(chip);
+               azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
+       }
+
+#if 0
+       /* clear state status int */
+       if (azx_readb(chip, STATESTS) & 0x04)
+               azx_writeb(chip, STATESTS, 0x04);
+#endif
+       spin_unlock(&chip->reg_lock);
+       
+       return IRQ_HANDLED;
+}
+
+
+/*
+ * set up BDL entries
+ */
+static void azx_setup_periods(azx_dev_t *azx_dev)
+{
+       u32 *bdl = azx_dev->bdl;
+       dma_addr_t dma_addr = azx_dev->substream->runtime->dma_addr;
+       int idx;
+
+       /* reset BDL address */
+       azx_sd_writel(azx_dev, SD_BDLPL, 0);
+       azx_sd_writel(azx_dev, SD_BDLPU, 0);
+
+       /* program the initial BDL entries */
+       for (idx = 0; idx < azx_dev->frags; idx++) {
+               unsigned int off = idx << 2; /* 4 dword step */
+               dma_addr_t addr = dma_addr + idx * azx_dev->fragsize;
+               /* program the address field of the BDL entry */
+               bdl[off] = cpu_to_le32((u32)addr);
+               bdl[off+1] = cpu_to_le32(upper_32bit(addr));
+
+               /* program the size field of the BDL entry */
+               bdl[off+2] = cpu_to_le32(azx_dev->fragsize);
+
+               /* program the IOC to enable interrupt when buffer completes */
+               bdl[off+3] = cpu_to_le32(0x01);
+       }
+}
+
+/*
+ * set up the SD for streaming
+ */
+static int azx_setup_controller(azx_t *chip, azx_dev_t *azx_dev)
+{
+       unsigned char val;
+       int timeout;
+
+       /* make sure the run bit is zero for SD */
+       azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & ~SD_CTL_DMA_START);
+       /* reset stream */
+       azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | SD_CTL_STREAM_RESET);
+       udelay(3);
+       timeout = 300;
+       while (!((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
+              --timeout)
+               ;
+       val &= ~SD_CTL_STREAM_RESET;
+       azx_sd_writeb(azx_dev, SD_CTL, val);
+       udelay(3);
+
+       timeout = 300;
+       /* waiting for hardware to report that the stream is out of reset */
+       while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
+              --timeout)
+               ;
+
+       /* program the stream_tag */
+       azx_sd_writel(azx_dev, SD_CTL,
+                     (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK) |
+                     (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT));
+
+       /* program the length of samples in cyclic buffer */
+       azx_sd_writel(azx_dev, SD_CBL, azx_dev->bufsize);
+
+       /* program the stream format */
+       /* this value needs to be the same as the one programmed */
+       azx_sd_writew(azx_dev, SD_FORMAT, azx_dev->format_val);
+
+       /* program the stream LVI (last valid index) of the BDL */
+       azx_sd_writew(azx_dev, SD_LVI, azx_dev->frags - 1);
+
+       /* program the BDL address */
+       /* lower BDL address */
+       azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl_addr);
+       /* upper BDL address */
+       azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr));
+
+#ifdef USE_POSBUF
+       /* enable the position buffer */
+       if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
+               azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
+#endif
+       /* set the interrupt enable bits in the descriptor control register */
+       azx_sd_writel(azx_dev, SD_CTL, azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK);
+
+       return 0;
+}
+
+
+/*
+ * Codec initialization
+ */
+
+static int __devinit azx_codec_create(azx_t *chip, const char *model)
+{
+       struct hda_bus_template bus_temp;
+       int c, codecs, err;
+
+       memset(&bus_temp, 0, sizeof(bus_temp));
+       bus_temp.private_data = chip;
+       bus_temp.modelname = model;
+       bus_temp.pci = chip->pci;
+       bus_temp.ops.command = azx_send_cmd;
+       bus_temp.ops.get_response = azx_get_response;
+
+       if ((err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus)) < 0)
+               return err;
+
+       codecs = 0;
+       for (c = 0; c < AZX_MAX_CODECS; c++) {
+               if (chip->codec_mask & (1 << c)) {
+                       err = snd_hda_codec_new(chip->bus, c, NULL);
+                       if (err < 0)
+                               continue;
+                       codecs++;
+               }
+       }
+       if (! codecs) {
+               snd_printk(KERN_ERR SFX "no codecs initialized\n");
+               return -ENXIO;
+       }
+
+       return 0;
+}
+
+
+/*
+ * PCM support
+ */
+
+/* assign a stream for the PCM */
+static inline azx_dev_t *azx_assign_device(azx_t *chip, int stream)
+{
+       int dev, i;
+       dev = stream == SNDRV_PCM_STREAM_PLAYBACK ? 4 : 0;
+       for (i = 0; i < 4; i++, dev++)
+               if (! chip->azx_dev[dev].opened) {
+                       chip->azx_dev[dev].opened = 1;
+                       return &chip->azx_dev[dev];
+               }
+       return NULL;
+}
+
+/* release the assigned stream */
+static inline void azx_release_device(azx_dev_t *azx_dev)
+{
+       azx_dev->opened = 0;
+}
+
+static snd_pcm_hardware_t azx_pcm_hw = {
+       .info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                SNDRV_PCM_INFO_MMAP_VALID |
+                                SNDRV_PCM_INFO_PAUSE |
+                                SNDRV_PCM_INFO_RESUME),
+       .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 =     AZX_MAX_BUF_SIZE,
+       .period_bytes_min =     128,
+       .period_bytes_max =     AZX_MAX_BUF_SIZE / 2,
+       .periods_min =          2,
+       .periods_max =          AZX_MAX_FRAG,
+       .fifo_size =            0,
+};
+
+struct azx_pcm {
+       azx_t *chip;
+       struct hda_codec *codec;
+       struct hda_pcm_stream *hinfo[2];
+};
+
+static int azx_pcm_open(snd_pcm_substream_t *substream)
+{
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+       azx_t *chip = apcm->chip;
+       azx_dev_t *azx_dev;
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       unsigned long flags;
+       int err;
+
+       down(&chip->open_mutex);
+       azx_dev = azx_assign_device(chip, substream->stream);
+       if (azx_dev == NULL) {
+               up(&chip->open_mutex);
+               return -EBUSY;
+       }
+       runtime->hw = azx_pcm_hw;
+       runtime->hw.channels_min = hinfo->channels_min;
+       runtime->hw.channels_max = hinfo->channels_max;
+       runtime->hw.formats = hinfo->formats;
+       runtime->hw.rates = hinfo->rates;
+       snd_pcm_limit_hw_rates(runtime);
+       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+       if ((err = hinfo->ops.open(hinfo, apcm->codec, substream)) < 0) {
+               azx_release_device(azx_dev);
+               up(&chip->open_mutex);
+               return err;
+       }
+       spin_lock_irqsave(&chip->reg_lock, flags);
+       azx_dev->substream = substream;
+       azx_dev->running = 0;
+       spin_unlock_irqrestore(&chip->reg_lock, flags);
+
+       runtime->private_data = azx_dev;
+       up(&chip->open_mutex);
+       return 0;
+}
+
+static int azx_pcm_close(snd_pcm_substream_t *substream)
+{
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+       azx_t *chip = apcm->chip;
+       azx_dev_t *azx_dev = get_azx_dev(substream);
+       unsigned long flags;
+
+       down(&chip->open_mutex);
+       spin_lock_irqsave(&chip->reg_lock, flags);
+       azx_dev->substream = NULL;
+       azx_dev->running = 0;
+       spin_unlock_irqrestore(&chip->reg_lock, flags);
+       azx_release_device(azx_dev);
+       hinfo->ops.close(hinfo, apcm->codec, substream);
+       up(&chip->open_mutex);
+       return 0;
+}
+
+static int azx_pcm_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 azx_pcm_hw_free(snd_pcm_substream_t *substream)
+{
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       azx_dev_t *azx_dev = get_azx_dev(substream);
+       struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+
+       /* reset BDL address */
+       azx_sd_writel(azx_dev, SD_BDLPL, 0);
+       azx_sd_writel(azx_dev, SD_BDLPU, 0);
+       azx_sd_writel(azx_dev, SD_CTL, 0);
+
+       hinfo->ops.cleanup(hinfo, apcm->codec, substream);
+
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int azx_pcm_prepare(snd_pcm_substream_t *substream)
+{
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       azx_t *chip = apcm->chip;
+       azx_dev_t *azx_dev = get_azx_dev(substream);
+       struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
+       snd_pcm_runtime_t *runtime = substream->runtime;
+
+       azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream);
+       azx_dev->fragsize = snd_pcm_lib_period_bytes(substream);
+       azx_dev->frags = azx_dev->bufsize / azx_dev->fragsize;
+       azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate,
+                                                        runtime->channels,
+                                                        runtime->format,
+                                                        hinfo->maxbps);
+       if (! azx_dev->format_val) {
+               snd_printk(KERN_ERR SFX "invalid format_val, rate=%d, ch=%d, format=%d\n",
+                          runtime->rate, runtime->channels, runtime->format);
+               return -EINVAL;
+       }
+
+       snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, format=0x%x\n",
+                   azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val);
+       azx_setup_periods(azx_dev);
+       azx_setup_controller(chip, azx_dev);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
+       else
+               azx_dev->fifo_size = 0;
+
+       return hinfo->ops.prepare(hinfo, apcm->codec, azx_dev->stream_tag,
+                                 azx_dev->format_val, substream);
+}
+
+static int azx_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       azx_dev_t *azx_dev = get_azx_dev(substream);
+       azx_t *chip = apcm->chip;
+       int err = 0;
+
+       spin_lock(&chip->reg_lock);
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_START:
+               azx_stream_start(chip, azx_dev);
+               azx_dev->running = 1;
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_STOP:
+               azx_stream_stop(chip, azx_dev);
+               azx_dev->running = 0;
+               break;
+       default:
+               err = -EINVAL;
+       }
+       spin_unlock(&chip->reg_lock);
+       if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH ||
+           cmd == SNDRV_PCM_TRIGGER_STOP) {
+               int timeout = 5000;
+               while (azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START && --timeout)
+                       ;
+       }
+       return err;
+}
+
+static snd_pcm_uframes_t azx_pcm_pointer(snd_pcm_substream_t *substream)
+{
+       azx_dev_t *azx_dev = get_azx_dev(substream);
+       unsigned int pos;
+
+#ifdef USE_POSBUF
+       /* use the position buffer */
+       pos = *azx_dev->posbuf;
+#else
+       /* read LPIB */
+       pos = azx_sd_readl(azx_dev, SD_LPIB) + azx_dev->fifo_size;
+#endif
+       if (pos >= azx_dev->bufsize)
+               pos = 0;
+       return bytes_to_frames(substream->runtime, pos);
+}
+
+static snd_pcm_ops_t azx_pcm_ops = {
+       .open = azx_pcm_open,
+       .close = azx_pcm_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = azx_pcm_hw_params,
+       .hw_free = azx_pcm_hw_free,
+       .prepare = azx_pcm_prepare,
+       .trigger = azx_pcm_trigger,
+       .pointer = azx_pcm_pointer,
+};
+
+static void azx_pcm_free(snd_pcm_t *pcm)
+{
+       kfree(pcm->private_data);
+}
+
+static int __devinit create_codec_pcm(azx_t *chip, struct hda_codec *codec,
+                                     struct hda_pcm *cpcm, int pcm_dev)
+{
+       int err;
+       snd_pcm_t *pcm;
+       struct azx_pcm *apcm;
+
+       snd_assert(cpcm->stream[0].substreams || cpcm->stream[1].substreams, return -EINVAL);
+       snd_assert(cpcm->name, return -EINVAL);
+
+       err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
+                         cpcm->stream[0].substreams, cpcm->stream[1].substreams,
+                         &pcm);
+       if (err < 0)
+               return err;
+       strcpy(pcm->name, cpcm->name);
+       apcm = kmalloc(sizeof(*apcm), GFP_KERNEL);
+       if (apcm == NULL)
+               return -ENOMEM;
+       apcm->chip = chip;
+       apcm->codec = codec;
+       apcm->hinfo[0] = &cpcm->stream[0];
+       apcm->hinfo[1] = &cpcm->stream[1];
+       pcm->private_data = apcm;
+       pcm->private_free = azx_pcm_free;
+       if (cpcm->stream[0].substreams)
+               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops);
+       if (cpcm->stream[1].substreams)
+               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                                             snd_dma_pci_data(chip->pci),
+                                             1024 * 64, 1024 * 128);
+       chip->pcm[pcm_dev] = pcm;
+
+       return 0;
+}
+
+static int __devinit azx_pcm_create(azx_t *chip)
+{
+       struct list_head *p;
+       struct hda_codec *codec;
+       int c, err;
+       int pcm_dev;
+
+       if ((err = snd_hda_build_pcms(chip->bus)) < 0)
+               return err;
+
+       pcm_dev = 0;
+       list_for_each(p, &chip->bus->codec_list) {
+               codec = list_entry(p, struct hda_codec, list);
+               for (c = 0; c < codec->num_pcms; c++) {
+                       if (pcm_dev >= AZX_MAX_PCMS) {
+                               snd_printk(KERN_ERR SFX "Too many PCMs\n");
+                               return -EINVAL;
+                       }
+                       err = create_codec_pcm(chip, codec, &codec->pcm_info[c], pcm_dev);
+                       if (err < 0)
+                               return err;
+                       pcm_dev++;
+               }
+       }
+       return 0;
+}
+
+/*
+ * mixer creation - all stuff is implemented in hda module
+ */
+static int __devinit azx_mixer_create(azx_t *chip)
+{
+       return snd_hda_build_controls(chip->bus);
+}
+
+
+/*
+ * initialize SD streams
+ */
+static int __devinit azx_init_stream(azx_t *chip)
+{
+       int i;
+
+       /* initialize each stream (aka device)
+        * assign the starting bdl address to each stream (device) and initialize
+        */
+       for (i = 0; i < MAX_ICH6_DEV; i++) {
+               unsigned int off = sizeof(u32) * (i * AZX_MAX_FRAG * 4);
+               azx_dev_t *azx_dev = &chip->azx_dev[i];
+               azx_dev->bdl = (u32 *)(chip->bdl.area + off);
+               azx_dev->bdl_addr = chip->bdl.addr + off;
+#ifdef USE_POSBUF
+               azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8);
+#endif
+               /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
+               azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
+               /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
+               azx_dev->sd_int_sta_mask = 1 << i;
+               /* stream tag: must be non-zero and unique */
+               azx_dev->index = i;
+               azx_dev->stream_tag = i + 1;
+       }
+
+       return 0;
+}
+
+
+#ifdef CONFIG_PM
+/*
+ * power management
+ */
+static int azx_suspend(snd_card_t *card, pm_message_t state)
+{
+       azx_t *chip = card->pm_private_data;
+       int i;
+
+       for (i = 0; i < chip->pcm_devs; i++)
+               if (chip->pcm[i])
+                       snd_pcm_suspend_all(chip->pcm[i]);
+       snd_hda_suspend(chip->bus, state);
+       azx_free_cmd_io(chip);
+       pci_disable_device(chip->pci);
+       return 0;
+}
+
+static int azx_resume(snd_card_t *card)
+{
+       azx_t *chip = card->pm_private_data;
+
+       pci_enable_device(chip->pci);
+       pci_set_master(chip->pci);
+       azx_init_chip(chip);
+       snd_hda_resume(chip->bus);
+       return 0;
+}
+#endif /* CONFIG_PM */
+
+
+/*
+ * destructor
+ */
+static int azx_free(azx_t *chip)
+{
+       if (chip->remap_addr) {
+               int i;
+
+               for (i = 0; i < MAX_ICH6_DEV; i++)
+                       azx_stream_stop(chip, &chip->azx_dev[i]);
+
+               /* disable interrupts */
+               azx_int_disable(chip);
+               azx_int_clear(chip);
+
+               /* disable CORB/RIRB */
+               azx_free_cmd_io(chip);
+
+               /* disable position buffer */
+               azx_writel(chip, DPLBASE, 0);
+               azx_writel(chip, DPUBASE, 0);
+
+               /* wait a little for interrupts to finish */
+               msleep(1);
+
+               iounmap(chip->remap_addr);
+       }
+
+       if (chip->irq >= 0)
+               free_irq(chip->irq, (void*)chip);
+
+       if (chip->bdl.area)
+               snd_dma_free_pages(&chip->bdl);
+       if (chip->rb.area)
+               snd_dma_free_pages(&chip->rb);
+#ifdef USE_POSBUF
+       if (chip->posbuf.area)
+               snd_dma_free_pages(&chip->posbuf);
+#endif
+       pci_release_regions(chip->pci);
+       pci_disable_device(chip->pci);
+       kfree(chip);
+
+       return 0;
+}
+
+static int azx_dev_free(snd_device_t *device)
+{
+       return azx_free(device->device_data);
+}
+
+/*
+ * constructor
+ */
+static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, azx_t **rchip)
+{
+       azx_t *chip;
+       int err = 0;
+       static snd_device_ops_t ops = {
+               .dev_free = azx_dev_free,
+       };
+
+       *rchip = NULL;
+       
+       if ((err = pci_enable_device(pci)) < 0)
+               return err;
+
+       chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+       
+       if (NULL == chip) {
+               snd_printk(KERN_ERR SFX "cannot allocate chip\n");
+               pci_disable_device(pci);
+               return -ENOMEM;
+       }
+
+       spin_lock_init(&chip->reg_lock);
+       init_MUTEX(&chip->open_mutex);
+       chip->card = card;
+       chip->pci = pci;
+       chip->irq = -1;
+
+       if ((err = pci_request_regions(pci, "ICH HD audio")) < 0) {
+               kfree(chip);
+               pci_disable_device(pci);
+               return err;
+       }
+
+       chip->addr = pci_resource_start(pci,0);
+       chip->remap_addr = ioremap_nocache(chip->addr, pci_resource_len(pci,0));
+       if (chip->remap_addr == NULL) {
+               snd_printk(KERN_ERR SFX "ioremap error\n");
+               err = -ENXIO;
+               goto errout;
+       }
+
+       if (request_irq(pci->irq, azx_interrupt, SA_INTERRUPT|SA_SHIRQ,
+                       "HDA Intel", (void*)chip)) {
+               snd_printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq);
+               err = -EBUSY;
+               goto errout;
+       }
+       chip->irq = pci->irq;
+
+       pci_set_master(pci);
+       synchronize_irq(chip->irq);
+
+       /* allocate memory for the BDL for each stream */
+       if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+                                      PAGE_SIZE, &chip->bdl)) < 0) {
+               snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
+               goto errout;
+       }
+#ifdef USE_POSBUF
+       /* allocate memory for the position buffer */
+       if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+                                      MAX_ICH6_DEV * 8, &chip->posbuf)) < 0) {
+               snd_printk(KERN_ERR SFX "cannot allocate posbuf\n");
+               goto errout;
+       }
+#endif
+       /* allocate CORB/RIRB */
+       if ((err = azx_alloc_cmd_io(chip)) < 0)
+               goto errout;
+
+       /* initialize streams */
+       azx_init_stream(chip);
+
+       /* initialize chip */
+       azx_init_chip(chip);
+
+       /* codec detection */
+       if (! chip->codec_mask) {
+               snd_printk(KERN_ERR SFX "no codecs found!\n");
+               err = -ENODEV;
+               goto errout;
+       }
+
+       if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) <0) {
+               snd_printk(KERN_ERR SFX "Error creating device [card]!\n");
+               goto errout;
+       }
+
+       *rchip = chip;
+       return 0;
+
+ errout:
+       azx_free(chip);
+       return err;
+}
+
+static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+       static int dev;
+       snd_card_t *card;
+       azx_t *chip;
+       int err = 0;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (! enable[dev]) {
+               dev++;
+               return -ENOENT;
+       }
+
+       card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+       if (NULL == card) {
+               snd_printk(KERN_ERR SFX "Error creating card!\n");
+               return -ENOMEM;
+       }
+
+       if ((err = azx_create(card, pci, &chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       strcpy(card->driver, "HDA-Intel");
+       strcpy(card->shortname, "HDA Intel");
+       sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->addr, chip->irq);
+
+       /* create codec instances */
+       if ((err = azx_codec_create(chip, model[dev])) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       /* create PCM streams */
+       if ((err = azx_pcm_create(chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       /* create mixer controls */
+       if ((err = azx_mixer_create(chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       snd_card_set_pm_callback(card, azx_suspend, azx_resume, chip);
+       snd_card_set_dev(card, &pci->dev);
+
+       if ((err = snd_card_register(card)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       pci_set_drvdata(pci, card);
+       dev++;
+
+       return err;
+}
+
+static void __devexit azx_remove(struct pci_dev *pci)
+{
+       snd_card_free(pci_get_drvdata(pci));
+       pci_set_drvdata(pci, NULL);
+}
+
+/* PCI IDs */
+static struct pci_device_id azx_ids[] = {
+       { 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH6 */
+       { 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH7 */
+       { 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ESB2 */
+       { 0, }
+};
+MODULE_DEVICE_TABLE(pci, azx_ids);
+
+/* pci_driver definition */
+static struct pci_driver driver = {
+       .name = "HDA Intel",
+       .id_table = azx_ids,
+       .probe = azx_probe,
+       .remove = __devexit_p(azx_remove),
+       SND_PCI_PM_CALLBACKS
+};
+
+static int __init alsa_card_azx_init(void)
+{
+       return pci_module_init(&driver);
+}
+
+static void __exit alsa_card_azx_exit(void)
+{
+       pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_azx_init)
+module_exit(alsa_card_azx_exit)
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
new file mode 100644 (file)
index 0000000..7c7b849
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * Local helper functions
+ *
+ * Copyright (c) 2004 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.
+ */
+
+#ifndef __SOUND_HDA_LOCAL_H
+#define __SOUND_HDA_LOCAL_H
+
+/*
+ * for mixer controls
+ */
+#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19))
+#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+       { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx,  \
+         .info = snd_hda_mixer_amp_volume_info, \
+         .get = snd_hda_mixer_amp_volume_get, \
+         .put = snd_hda_mixer_amp_volume_put, \
+         .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \
+       HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction)
+#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \
+       HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \
+       HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction)
+#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+       { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+         .info = snd_hda_mixer_amp_switch_info, \
+         .get = snd_hda_mixer_amp_switch_get, \
+         .put = snd_hda_mixer_amp_switch_put, \
+         .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+#define HDA_CODEC_MUTE_IDX(xname, xcidx, nid, xindex, direction) \
+       HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, 3, xindex, direction)
+#define HDA_CODEC_MUTE_MONO(xname, nid, channel, xindex, direction) \
+       HDA_CODEC_MUTE_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+#define HDA_CODEC_MUTE(xname, nid, xindex, direction) \
+       HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction)
+
+int snd_hda_mixer_amp_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo);
+int snd_hda_mixer_amp_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
+int snd_hda_mixer_amp_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
+int snd_hda_mixer_amp_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo);
+int snd_hda_mixer_amp_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
+int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol);
+
+int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
+
+/*
+ * input MUX helper
+ */
+#define HDA_MAX_NUM_INPUTS     8
+struct hda_input_mux_item {
+       const char *label;
+       unsigned int index;
+};
+struct hda_input_mux {
+       unsigned int num_items;
+       struct hda_input_mux_item items[HDA_MAX_NUM_INPUTS];
+};
+
+int snd_hda_input_mux_info(const struct hda_input_mux *imux, snd_ctl_elem_info_t *uinfo);
+int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux,
+                         snd_ctl_elem_value_t *ucontrol, hda_nid_t nid,
+                         unsigned int *cur_val);
+
+/*
+ * Multi-channel / digital-out PCM helper
+ */
+
+enum { HDA_FRONT, HDA_REAR, HDA_CLFE, HDA_SIDE }; /* index for dac_nidx */
+enum { HDA_DIG_NONE, HDA_DIG_EXCLUSIVE, HDA_DIG_ANALOG_DUP }; /* dig_out_used */
+
+struct hda_multi_out {
+       int num_dacs;           /* # of DACs, must be more than 1 */
+       hda_nid_t *dac_nids;    /* DAC list */
+       hda_nid_t hp_nid;       /* optional DAC for HP, 0 when not exists */
+       hda_nid_t dig_out_nid;  /* digital out audio widget */
+       int max_channels;       /* currently supported analog channels */
+       int dig_out_used;       /* current usage of digital out (HDA_DIG_XXX) */
+};
+
+int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout);
+int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout);
+int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout,
+                                 snd_pcm_substream_t *substream);
+int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout,
+                                    unsigned int stream_tag,
+                                    unsigned int format,
+                                    snd_pcm_substream_t *substream);
+int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout);
+
+/*
+ * generic codec parser
+ */
+int snd_hda_parse_generic_codec(struct hda_codec *codec);
+
+/*
+ * generic proc interface
+ */
+#ifdef CONFIG_PROC_FS
+int snd_hda_codec_proc_new(struct hda_codec *codec);
+#else
+static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
+#endif
+
+/*
+ * Misc
+ */
+struct hda_board_config {
+       const char *modelname;
+       int config;
+       unsigned short pci_vendor;
+       unsigned short pci_device;
+};
+
+int snd_hda_check_board_config(struct hda_codec *codec, struct hda_board_config *tbl);
+int snd_hda_add_new_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew);
+
+/*
+ * power management
+ */
+#ifdef CONFIG_PM
+int snd_hda_resume_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew);
+int snd_hda_resume_spdif_out(struct hda_codec *codec);
+int snd_hda_resume_spdif_in(struct hda_codec *codec);
+#endif
+
+/*
+ * unsolicited event handler
+ */
+
+#define HDA_UNSOL_QUEUE_SIZE   64
+
+struct hda_bus_unsolicited {
+       /* ring buffer */
+       u32 queue[HDA_UNSOL_QUEUE_SIZE * 2];
+       unsigned int rp, wp;
+
+       /* workqueue */
+       struct workqueue_struct *workq;
+       struct work_struct work;
+};
+
+#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h
new file mode 100644 (file)
index 0000000..cf6abce
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * HDA Patches - included by hda_codec.c
+ */
+
+/* Realtek codecs */
+extern struct hda_codec_preset snd_hda_preset_realtek[];
+/* C-Media codecs */
+extern struct hda_codec_preset snd_hda_preset_cmedia[];
+/* Analog Devices codecs */
+extern struct hda_codec_preset snd_hda_preset_analog[];
+
+static const struct hda_codec_preset *hda_preset_tables[] = {
+       snd_hda_preset_realtek,
+       snd_hda_preset_cmedia,
+       snd_hda_preset_analog,
+       NULL
+};
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
new file mode 100644 (file)
index 0000000..4d5db7f
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ * 
+ * Generic proc interface
+ *
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the 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 driver 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/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+
+static const char *get_wid_type_name(unsigned int wid_value)
+{
+       static char *names[16] = {
+               [AC_WID_AUD_OUT] = "Audio Output",
+               [AC_WID_AUD_IN] = "Audio Input",
+               [AC_WID_AUD_MIX] = "Audio Mixer",
+               [AC_WID_AUD_SEL] = "Audio Selector",
+               [AC_WID_PIN] = "Pin Complex",
+               [AC_WID_POWER] = "Power Widget",
+               [AC_WID_VOL_KNB] = "Volume Knob Widget",
+               [AC_WID_BEEP] = "Beep Generator Widget",
+               [AC_WID_VENDOR] = "Vendor Defined Widget",
+       };
+       wid_value &= 0xf;
+       if (names[wid_value])
+               return names[wid_value];
+       else
+               return "UNKOWN Widget";
+}
+
+static void print_amp_caps(snd_info_buffer_t *buffer,
+                          struct hda_codec *codec, hda_nid_t nid, int dir)
+{
+       unsigned int caps;
+       if (dir == HDA_OUTPUT)
+               caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_OUT_CAP);
+       else
+               caps = snd_hda_param_read(codec, nid, AC_PAR_AMP_IN_CAP);
+       if (caps == -1 || caps == 0) {
+               snd_iprintf(buffer, "N/A\n");
+               return;
+       }
+       snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, mute=%x\n",
+                   caps & AC_AMPCAP_OFFSET,
+                   (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT,
+                   (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT,
+                   (caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT);
+}
+
+static void print_amp_vals(snd_info_buffer_t *buffer,
+                          struct hda_codec *codec, hda_nid_t nid,
+                          int dir, int stereo)
+{
+       unsigned int val;
+       if (stereo) {
+               val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE,
+                                         AC_AMP_GET_LEFT |
+                                        (dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT :
+                                         AC_AMP_GET_INPUT));
+               snd_iprintf(buffer, "0x%02x ", val);
+       }
+       val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE,
+                                AC_AMP_GET_RIGHT |
+                                (dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT :
+                                 AC_AMP_GET_INPUT));
+       snd_iprintf(buffer, "0x%02x\n", val);
+}
+
+static void print_pcm_caps(snd_info_buffer_t *buffer,
+                          struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM);
+       unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+       if (pcm == -1 || stream == -1) {
+               snd_iprintf(buffer, "N/A\n");
+               return;
+       }
+       snd_iprintf(buffer, "rates 0x%03x, bits 0x%02x, types 0x%x\n",
+                   pcm & AC_SUPPCM_RATES, (pcm >> 16) & 0xff, stream & 0xf);
+}
+
+static const char *get_jack_location(u32 cfg)
+{
+       static char *bases[7] = {
+               "N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
+       };
+       static unsigned char specials_idx[] = {
+               0x07, 0x08,
+               0x17, 0x18, 0x19,
+               0x37, 0x38
+       };
+       static char *specials[] = {
+               "Rear Panel", "Drive Bar",
+               "Riser", "HDMI", "ATAPI",
+               "Mobile-In", "Mobile-Out"
+       };
+       int i;
+       cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
+       if ((cfg & 0x0f) < 7)
+               return bases[cfg & 0x0f];
+       for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
+               if (cfg == specials_idx[i])
+                       return specials[i];
+       }
+       return "UNKNOWN";
+}
+
+static const char *get_jack_connection(u32 cfg)
+{
+       static char *names[16] = {
+               "Unknown", "1/8", "1/4", "ATAPI",
+               "RCA", "Optical","Digital", "Analog",
+               "DIN", "XLR", "RJ11", "Comb",
+               NULL, NULL, NULL, "Other"
+       };
+       cfg = (cfg & AC_DEFCFG_CONN_TYPE) >> AC_DEFCFG_CONN_TYPE_SHIFT;
+       if (names[cfg])
+               return names[cfg];
+       else
+               return "UNKNOWN";
+}
+
+static const char *get_jack_color(u32 cfg)
+{
+       static char *names[16] = {
+               "Unknown", "Black", "Grey", "Blue",
+               "Green", "Red", "Orange", "Yellow",
+               "Purple", "Pink", NULL, NULL,
+               NULL, NULL, "White", "Other",
+       };
+       cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT;
+       if (names[cfg])
+               return names[cfg];
+       else
+               return "UNKNOWN";
+}
+
+static void print_pin_caps(snd_info_buffer_t *buffer,
+                          struct hda_codec *codec, hda_nid_t nid)
+{
+       static char *jack_types[16] = {
+               "Line Out", "Speaker", "HP Out", "CD",
+               "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
+               "Line In", "Aux", "Mic", "Telephony",
+               "SPDIF In", "Digitial In", "Reserved", "Other"
+       };
+       static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
+       unsigned int caps;
+
+       caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+       snd_iprintf(buffer, "  Pincap 0x08%x:", caps);
+       if (caps & AC_PINCAP_IN)
+               snd_iprintf(buffer, " IN");
+       if (caps & AC_PINCAP_OUT)
+               snd_iprintf(buffer, " OUT");
+       if (caps & AC_PINCAP_HP_DRV)
+               snd_iprintf(buffer, " HP");
+       snd_iprintf(buffer, "\n");
+       caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+       snd_iprintf(buffer, "  Pin Default 0x%08x: %s at %s %s\n", caps,
+                   jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT],
+                   jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3],
+                   get_jack_location(caps));
+       snd_iprintf(buffer, "    Conn = %s, Color = %s\n",
+                   get_jack_connection(caps),
+                   get_jack_color(caps));
+}
+
+
+static void print_codec_info(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+       struct hda_codec *codec = entry->private_data;
+       char buf[32];
+       hda_nid_t nid;
+       int i, nodes;
+
+       snd_hda_get_codec_name(codec, buf, sizeof(buf));
+       snd_iprintf(buffer, "Codec: %s\n", buf);
+       snd_iprintf(buffer, "Address: %d\n", codec->addr);
+       snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
+       snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
+       snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
+       snd_iprintf(buffer, "Default PCM: ");
+       print_pcm_caps(buffer, codec, codec->afg);
+       snd_iprintf(buffer, "Default Amp-In caps: ");
+       print_amp_caps(buffer, codec, codec->afg, HDA_INPUT);
+       snd_iprintf(buffer, "Default Amp-Out caps: ");
+       print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT);
+
+       nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+       if (! nid || nodes < 0) {
+               snd_iprintf(buffer, "Invalid AFG subtree\n");
+               return;
+       }
+       for (i = 0; i < nodes; i++, nid++) {
+               unsigned int wid_caps = snd_hda_param_read(codec, nid,
+                                                          AC_PAR_AUDIO_WIDGET_CAP);
+               unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+               snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
+                           get_wid_type_name(wid_type), wid_caps);
+               if (wid_caps & AC_WCAP_STEREO)
+                       snd_iprintf(buffer, " Stereo");
+               else
+                       snd_iprintf(buffer, " Mono");
+               if (wid_caps & AC_WCAP_DIGITAL)
+                       snd_iprintf(buffer, " Digital");
+               if (wid_caps & AC_WCAP_IN_AMP)
+                       snd_iprintf(buffer, " Amp-In");
+               if (wid_caps & AC_WCAP_OUT_AMP)
+                       snd_iprintf(buffer, " Amp-Out");
+               snd_iprintf(buffer, "\n");
+
+               if (wid_caps & AC_WCAP_IN_AMP) {
+                       snd_iprintf(buffer, "  Amp-In caps: ");
+                       print_amp_caps(buffer, codec, nid, HDA_INPUT);
+                       snd_iprintf(buffer, "  Amp-In vals: ");
+                       print_amp_vals(buffer, codec, nid, HDA_INPUT,
+                                      wid_caps & AC_WCAP_STEREO);
+               }
+               if (wid_caps & AC_WCAP_OUT_AMP) {
+                       snd_iprintf(buffer, "  Amp-Out caps: ");
+                       print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
+                       snd_iprintf(buffer, "  Amp-Out vals: ");
+                       print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
+                                      wid_caps & AC_WCAP_STEREO);
+               }
+
+               if (wid_type == AC_WID_PIN) {
+                       unsigned int pinctls;
+                       print_pin_caps(buffer, codec, nid);
+                       pinctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+                       snd_iprintf(buffer, "  Pin-ctls: 0x%02x:", pinctls);
+                       if (pinctls & AC_PINCTL_IN_EN)
+                               snd_iprintf(buffer, " IN");
+                       if (pinctls & AC_PINCTL_OUT_EN)
+                               snd_iprintf(buffer, " OUT");
+                       if (pinctls & AC_PINCTL_HP_EN)
+                               snd_iprintf(buffer, " HP");
+                       snd_iprintf(buffer, "\n");
+               }
+
+               if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) &&
+                   (wid_caps & AC_WCAP_FORMAT_OVRD)) {
+                       snd_iprintf(buffer, "  PCM: ");
+                       print_pcm_caps(buffer, codec, nid);
+               }
+
+               if (wid_caps & AC_WCAP_CONN_LIST) {
+                       hda_nid_t conn[HDA_MAX_CONNECTIONS];
+                       int c, conn_len;
+                       conn_len = snd_hda_get_connections(codec, nid, conn,
+                                                          HDA_MAX_CONNECTIONS);
+                       snd_iprintf(buffer, "  Connection: %d\n", conn_len);
+                       snd_iprintf(buffer, "    ");
+                       for (c = 0; c < conn_len; c++)
+                               snd_iprintf(buffer, " 0x%02x", conn[c]);
+                       snd_iprintf(buffer, "\n");
+               }
+       }
+}
+
+/*
+ * create a proc read
+ */
+int snd_hda_codec_proc_new(struct hda_codec *codec)
+{
+       char name[32];
+       snd_info_entry_t *entry;
+       int err;
+
+       snprintf(name, sizeof(name), "codec#%d", codec->addr);
+       err = snd_card_proc_new(codec->bus->card, name, &entry);
+       if (err < 0)
+               return err;
+
+       snd_info_set_text_ops(entry, codec, 32 * 1024, print_codec_info);
+       return 0;
+}
+
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
new file mode 100644 (file)
index 0000000..75d2384
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+ * HD audio interface patch for AD1986A
+ *
+ * Copyright (c) 2005 Takashi Iwai <tiwai@suse.de>
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the 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 driver 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/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+struct ad1986a_spec {
+       struct semaphore amp_mutex;     /* PCM volume/mute control mutex */
+       struct hda_multi_out multiout;  /* playback */
+       unsigned int cur_mux;           /* capture source */
+       struct hda_pcm pcm_rec[2];      /* PCM information */
+};
+
+#define AD1986A_SPDIF_OUT      0x02
+#define AD1986A_FRONT_DAC      0x03
+#define AD1986A_SURR_DAC       0x04
+#define AD1986A_CLFE_DAC       0x05
+#define AD1986A_ADC            0x06
+
+static hda_nid_t ad1986a_dac_nids[3] = {
+       AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
+};
+
+static struct hda_input_mux ad1986a_capture_source = {
+       .num_items = 7,
+       .items = {
+               { "Mic", 0x0 },
+               { "CD", 0x1 },
+               { "Aux", 0x3 },
+               { "Line", 0x4 },
+               { "Mix", 0x5 },
+               { "Mono", 0x6 },
+               { "Phone", 0x7 },
+       },
+};
+
+/*
+ * PCM control
+ *
+ * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
+ */
+
+#define ad1986a_pcm_amp_vol_info       snd_hda_mixer_amp_volume_info
+
+static int ad1986a_pcm_amp_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ad1986a_spec *ad = codec->spec;
+
+       down(&ad->amp_mutex);
+       snd_hda_mixer_amp_volume_get(kcontrol, ucontrol);
+       up(&ad->amp_mutex);
+       return 0;
+}
+
+static int ad1986a_pcm_amp_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ad1986a_spec *ad = codec->spec;
+       int i, change = 0;
+
+       down(&ad->amp_mutex);
+       for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) {
+               kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT);
+               change |= snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
+       }
+       kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT);
+       up(&ad->amp_mutex);
+       return change;
+}
+
+#define ad1986a_pcm_amp_sw_info                snd_hda_mixer_amp_volume_info
+
+static int ad1986a_pcm_amp_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ad1986a_spec *ad = codec->spec;
+
+       down(&ad->amp_mutex);
+       snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
+       up(&ad->amp_mutex);
+       return 0;
+}
+
+static int ad1986a_pcm_amp_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ad1986a_spec *ad = codec->spec;
+       int i, change = 0;
+
+       down(&ad->amp_mutex);
+       for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) {
+               kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT);
+               change |= snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+       }
+       kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT);
+       up(&ad->amp_mutex);
+       return change;
+}
+
+/*
+ * input MUX handling
+ */
+static int ad1986a_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       return snd_hda_input_mux_info(&ad1986a_capture_source, uinfo);
+}
+
+static int ad1986a_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ad1986a_spec *spec = codec->spec;
+
+       ucontrol->value.enumerated.item[0] = spec->cur_mux;
+       return 0;
+}
+
+static int ad1986a_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ad1986a_spec *spec = codec->spec;
+
+       return snd_hda_input_mux_put(codec, &ad1986a_capture_source, ucontrol,
+                                    AD1986A_ADC, &spec->cur_mux);
+}
+
+/*
+ * mixers
+ */
+static snd_kcontrol_new_t ad1986a_mixers[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "PCM Playback Volume",
+               .info = ad1986a_pcm_amp_vol_info,
+               .get = ad1986a_pcm_amp_vol_get,
+               .put = ad1986a_pcm_amp_vol_put,
+               .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "PCM Playback Switch",
+               .info = ad1986a_pcm_amp_sw_info,
+               .get = ad1986a_pcm_amp_sw_get,
+               .put = ad1986a_pcm_amp_sw_put,
+               .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT)
+       },
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x18, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x18, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Capture Source",
+               .info = ad1986a_mux_enum_info,
+               .get = ad1986a_mux_enum_get,
+               .put = ad1986a_mux_enum_put,
+       },
+       HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
+       { } /* end */
+};
+
+/*
+ * initialization verbs
+ */
+static struct hda_verb ad1986a_init_verbs[] = {
+       /* Front, Surround, CLFE DAC; mute as default */
+       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* Downmix - off */
+       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* HP, Line-Out, Surround, CLFE selectors */
+       {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
+       {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
+       {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
+       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* Mono selector */
+       {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* Mic selector: Mic 1/2 pin */
+       {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* Line-in selector: Line-in */
+       {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* Mic 1/2 swap */
+       {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* Record selector: mic */
+       {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
+       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* PC beep */
+       {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       { } /* end */
+};
+
+
+static int ad1986a_init(struct hda_codec *codec)
+{
+       snd_hda_sequence_write(codec, ad1986a_init_verbs);
+       return 0;
+}
+
+static int ad1986a_build_controls(struct hda_codec *codec)
+{
+       int err;
+
+       err = snd_hda_add_new_ctls(codec, ad1986a_mixers);
+       if (err < 0)
+               return err;
+       err = snd_hda_create_spdif_out_ctls(codec, AD1986A_SPDIF_OUT);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+/*
+ * Analog playback callbacks
+ */
+static int ad1986a_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                    struct hda_codec *codec,
+                                    snd_pcm_substream_t *substream)
+{
+       struct ad1986a_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int ad1986a_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       unsigned int stream_tag,
+                                       unsigned int format,
+                                       snd_pcm_substream_t *substream)
+{
+       struct ad1986a_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+                                               format, substream);
+}
+
+static int ad1986a_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       snd_pcm_substream_t *substream)
+{
+       struct ad1986a_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int ad1986a_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                        struct hda_codec *codec,
+                                        snd_pcm_substream_t *substream)
+{
+       struct ad1986a_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int ad1986a_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+                                         struct hda_codec *codec,
+                                         snd_pcm_substream_t *substream)
+{
+       struct ad1986a_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int ad1986a_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      unsigned int stream_tag,
+                                      unsigned int format,
+                                      snd_pcm_substream_t *substream)
+{
+       snd_hda_codec_setup_stream(codec, AD1986A_ADC, stream_tag, 0, format);
+       return 0;
+}
+
+static int ad1986a_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      snd_pcm_substream_t *substream)
+{
+       snd_hda_codec_setup_stream(codec, AD1986A_ADC, 0, 0, 0);
+       return 0;
+}
+
+
+/*
+ */
+static struct hda_pcm_stream ad1986a_pcm_analog_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 6,
+       .nid = AD1986A_FRONT_DAC, /* NID to query formats and rates */
+       .ops = {
+               .open = ad1986a_playback_pcm_open,
+               .prepare = ad1986a_playback_pcm_prepare,
+               .cleanup = ad1986a_playback_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream ad1986a_pcm_analog_capture = {
+       .substreams = 2,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = AD1986A_ADC, /* NID to query formats and rates */
+       .ops = {
+               .prepare = ad1986a_capture_pcm_prepare,
+               .cleanup = ad1986a_capture_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream ad1986a_pcm_digital_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = AD1986A_SPDIF_OUT, 
+       .ops = {
+               .open = ad1986a_dig_playback_pcm_open,
+               .close = ad1986a_dig_playback_pcm_close
+       },
+};
+
+static int ad1986a_build_pcms(struct hda_codec *codec)
+{
+       struct ad1986a_spec *spec = codec->spec;
+       struct hda_pcm *info = spec->pcm_rec;
+
+       codec->num_pcms = 2;
+       codec->pcm_info = info;
+
+       info->name = "AD1986A Analog";
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad1986a_pcm_analog_playback;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1986a_pcm_analog_capture;
+       info++;
+
+       info->name = "AD1986A Digital";
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad1986a_pcm_digital_playback;
+
+       return 0;
+}
+
+static void ad1986a_free(struct hda_codec *codec)
+{
+       kfree(codec->spec);
+}
+
+#ifdef CONFIG_PM
+static int ad1986a_resume(struct hda_codec *codec)
+{
+       ad1986a_init(codec);
+       snd_hda_resume_ctls(codec, ad1986a_mixers);
+       snd_hda_resume_spdif_out(codec);
+       return 0;
+}
+#endif
+
+static struct hda_codec_ops ad1986a_patch_ops = {
+       .build_controls = ad1986a_build_controls,
+       .build_pcms = ad1986a_build_pcms,
+       .init = ad1986a_init,
+       .free = ad1986a_free,
+#ifdef CONFIG_PM
+       .resume = ad1986a_resume,
+#endif
+};
+
+static int patch_ad1986a(struct hda_codec *codec)
+{
+       struct ad1986a_spec *spec;
+
+       spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       init_MUTEX(&spec->amp_mutex);
+       codec->spec = spec;
+
+       spec->multiout.max_channels = 6;
+       spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
+       spec->multiout.dac_nids = ad1986a_dac_nids;
+       spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
+
+       codec->patch_ops = ad1986a_patch_ops;
+
+       return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_analog[] = {
+       { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
+       {} /* terminator */
+};
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
new file mode 100644 (file)
index 0000000..17c5062
--- /dev/null
@@ -0,0 +1,1503 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for ALC 260/880/882 codecs
+ *
+ * Copyright (c) 2004 PeiSen Hou <pshou@realtek.com.tw>
+ *                    Takashi Iwai <tiwai@suse.de>
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the 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 driver 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/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+
+/* ALC880 board config type */
+enum {
+       ALC880_MINIMAL,
+       ALC880_3ST,
+       ALC880_3ST_DIG,
+       ALC880_5ST,
+       ALC880_5ST_DIG,
+       ALC880_W810,
+};
+
+struct alc_spec {
+       /* codec parameterization */
+       unsigned int front_panel: 1;
+
+       snd_kcontrol_new_t* mixers[2];
+       unsigned int num_mixers;
+
+       struct hda_verb *init_verbs;
+
+       char* stream_name_analog;
+       struct hda_pcm_stream *stream_analog_playback;
+       struct hda_pcm_stream *stream_analog_capture;
+
+       char* stream_name_digital;
+       struct hda_pcm_stream *stream_digital_playback;
+       struct hda_pcm_stream *stream_digital_capture;
+
+       /* playback */
+       struct hda_multi_out multiout;
+
+       /* capture */
+       unsigned int num_adc_nids;
+       hda_nid_t *adc_nids;
+       hda_nid_t dig_in_nid;
+
+       /* capture source */
+       const struct hda_input_mux *input_mux;
+       unsigned int cur_mux[3];
+
+       /* channel model */
+       const struct alc_channel_mode *channel_mode;
+       int num_channel_mode;
+
+       /* PCM information */
+       struct hda_pcm pcm_rec[2];
+};
+
+/* DAC/ADC assignment */
+
+static hda_nid_t alc880_dac_nids[4] = {
+       /* front, rear, clfe, rear_surr */
+       0x02, 0x05, 0x04, 0x03
+};
+
+static hda_nid_t alc880_w810_dac_nids[3] = {
+       /* front, rear/surround, clfe */
+       0x02, 0x03, 0x04
+};
+
+static hda_nid_t alc880_adc_nids[3] = {
+       /* ADC0-2 */
+       0x07, 0x08, 0x09,
+};
+
+#define ALC880_DIGOUT_NID      0x06
+#define ALC880_DIGIN_NID       0x0a
+
+static hda_nid_t alc260_dac_nids[1] = {
+       /* front */
+       0x02,
+};
+
+static hda_nid_t alc260_adc_nids[2] = {
+       /* ADC0-1 */
+       0x04, 0x05,
+};
+
+#define ALC260_DIGOUT_NID      0x03
+#define ALC260_DIGIN_NID       0x06
+
+static struct hda_input_mux alc880_capture_source = {
+       .num_items = 4,
+       .items = {
+               { "Mic", 0x0 },
+               { "Front Mic", 0x3 },
+               { "Line", 0x2 },
+               { "CD", 0x4 },
+       },
+};
+
+static struct hda_input_mux alc260_capture_source = {
+       .num_items = 4,
+       .items = {
+               { "Mic", 0x0 },
+               { "Front Mic", 0x1 },
+               { "Line", 0x2 },
+               { "CD", 0x4 },
+       },
+};
+
+/*
+ * input MUX handling
+ */
+static int alc_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       return snd_hda_input_mux_info(spec->input_mux, uinfo);
+}
+
+static int alc_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+       ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+       return 0;
+}
+
+static int alc_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+       return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+                                    spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
+}
+
+/*
+ * channel mode setting
+ */
+struct alc_channel_mode {
+       int channels;
+       const struct hda_verb *sequence;
+};
+
+
+/*
+ * channel source setting (2/6 channel selection for 3-stack)
+ */
+
+/*
+ * set the path ways for 2 channel output
+ * need to set the codec line out and mic 1 pin widgets to inputs
+ */
+static struct hda_verb alc880_threestack_ch2_init[] = {
+       /* set pin widget 1Ah (line in) for input */
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+       /* set pin widget 18h (mic1) for input, for mic also enable the vref */
+       { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+       /* mute the output for Line In PW */
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
+       /* mute for Mic1 PW */
+       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
+       { } /* end */
+};
+
+/*
+ * 6ch mode
+ * need to set the codec line out and mic 1 pin widgets to outputs
+ */
+static struct hda_verb alc880_threestack_ch6_init[] = {
+       /* set pin widget 1Ah (line in) for output */
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       /* set pin widget 18h (mic1) for output */
+       { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       /* unmute the output for Line In PW */
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 },
+       /* unmute for Mic1 PW */
+       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 },
+       /* for rear channel output using Line In 1
+        * set select widget connection (nid = 0x12) - to summer node
+        * for rear NID = 0x0f...offset 3 in connection list
+        */
+       { 0x12, AC_VERB_SET_CONNECT_SEL, 0x3 },
+       /* for Mic1 - retask for center/lfe */
+       /* set select widget connection (nid = 0x10) - to summer node for
+        * front CLFE NID = 0x0e...offset 2 in connection list
+        */
+       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x2 },
+       { } /* end */
+};
+
+static struct alc_channel_mode alc880_threestack_modes[2] = {
+       { 2, alc880_threestack_ch2_init },
+       { 6, alc880_threestack_ch6_init },
+};
+
+
+/*
+ * channel source setting (6/8 channel selection for 5-stack)
+ */
+
+/* set the path ways for 6 channel output
+ * need to set the codec line out and mic 1 pin widgets to inputs
+ */
+static struct hda_verb alc880_fivestack_ch6_init[] = {
+       /* set pin widget 1Ah (line in) for input */
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+       /* mute the output for Line In PW */
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
+       { } /* end */
+};
+
+/* need to set the codec line out and mic 1 pin widgets to outputs */
+static struct hda_verb alc880_fivestack_ch8_init[] = {
+       /* set pin widget 1Ah (line in) for output */
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       /* unmute the output for Line In PW */
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 },
+       /* output for surround channel output using Line In 1 */
+       /* set select widget connection (nid = 0x12) - to summer node
+        * for surr_rear NID = 0x0d...offset 1 in connection list
+        */
+       { 0x12, AC_VERB_SET_CONNECT_SEL, 0x1 },
+       { } /* end */
+};
+
+static struct alc_channel_mode alc880_fivestack_modes[2] = {
+       { 6, alc880_fivestack_ch6_init },
+       { 8, alc880_fivestack_ch8_init },
+};
+
+/*
+ * channel source setting for W810 system
+ *
+ * W810 has rear IO for:
+ * Front (DAC 02)
+ * Surround (DAC 03)
+ * Center/LFE (DAC 04)
+ * Digital out (06)
+ *
+ * The system also has a pair of internal speakers, and a headphone jack.
+ * These are both connected to Line2 on the codec, hence to DAC 02.
+ * 
+ * There is a variable resistor to control the speaker or headphone
+ * volume. This is a hardware-only device without a software API.
+ *
+ * Plugging headphones in will disable the internal speakers. This is
+ * implemented in hardware, not via the driver using jack sense. In
+ * a similar fashion, plugging into the rear socket marked "front" will
+ * disable both the speakers and headphones.
+ *
+ * For input, there's a microphone jack, and an "audio in" jack.
+ * These may not do anything useful with this driver yet, because I
+ * haven't setup any initialization verbs for these yet...
+ */
+
+static struct alc_channel_mode alc880_w810_modes[1] = {
+       { 6, NULL }
+};
+
+/*
+ */
+static int alc880_ch_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+
+       snd_assert(spec->channel_mode, return -ENXIO);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+       if (uinfo->value.enumerated.item >= 2)
+               uinfo->value.enumerated.item = 1;
+       sprintf(uinfo->value.enumerated.name, "%dch",
+               spec->channel_mode[uinfo->value.enumerated.item].channels);
+       return 0;
+}
+
+static int alc880_ch_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+
+       snd_assert(spec->channel_mode, return -ENXIO);
+       ucontrol->value.enumerated.item[0] =
+               (spec->multiout.max_channels == spec->channel_mode[0].channels) ? 0 : 1;
+       return 0;
+}
+
+static int alc880_ch_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       int mode;
+
+       snd_assert(spec->channel_mode, return -ENXIO);
+       mode = ucontrol->value.enumerated.item[0] ? 1 : 0;
+       if (spec->multiout.max_channels == spec->channel_mode[mode].channels &&
+           ! codec->in_resume)
+               return 0;
+
+       /* change the current channel setting */
+       spec->multiout.max_channels = spec->channel_mode[mode].channels;
+       if (spec->channel_mode[mode].sequence)
+               snd_hda_sequence_write(codec, spec->channel_mode[mode].sequence);
+
+       return 1;
+}
+
+
+/*
+ */
+
+/* 3-stack mode
+ * Pin assignment: Front=0x14, Line-In/Rear=0x1a, Mic/CLFE=0x18, F-Mic=0x1b
+ *                 HP=0x19
+ */
+static snd_kcontrol_new_t alc880_base_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Surround Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x18, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x18, 2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
+       HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+       HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               /* The multiple "Capture Source" controls confuse alsamixer
+                * So call somewhat different..
+                * FIXME: the controls appear in the "playback" view!
+                */
+               /* .name = "Capture Source", */
+               .name = "Input Source",
+               .count = 2,
+               .info = alc_mux_enum_info,
+               .get = alc_mux_enum_get,
+               .put = alc_mux_enum_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Channel Mode",
+               .info = alc880_ch_mode_info,
+               .get = alc880_ch_mode_get,
+               .put = alc880_ch_mode_put,
+       },
+       { } /* end */
+};
+
+/* 5-stack mode
+ * Pin assignment: Front=0x14, Rear=0x17, CLFE=0x16
+ *                 Line-In/Side=0x1a, Mic=0x18, F-Mic=0x1b, HP=0x19
+ */
+static snd_kcontrol_new_t alc880_five_stack_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Surround Playback Switch", 0x17, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Side Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
+       HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+       HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               /* The multiple "Capture Source" controls confuse alsamixer
+                * So call somewhat different..
+                * FIXME: the controls appear in the "playback" view!
+                */
+               /* .name = "Capture Source", */
+               .name = "Input Source",
+               .count = 2,
+               .info = alc_mux_enum_info,
+               .get = alc_mux_enum_get,
+               .put = alc_mux_enum_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Channel Mode",
+               .info = alc880_ch_mode_info,
+               .get = alc880_ch_mode_get,
+               .put = alc880_ch_mode_put,
+       },
+       { } /* end */
+};
+
+static snd_kcontrol_new_t alc880_w810_base_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               /* The multiple "Capture Source" controls confuse alsamixer
+                * So call somewhat different..
+                * FIXME: the controls appear in the "playback" view!
+                */
+               /* .name = "Capture Source", */
+               .name = "Input Source",
+               .count = 3,
+               .info = alc_mux_enum_info,
+               .get = alc_mux_enum_get,
+               .put = alc_mux_enum_put,
+       },
+       { } /* end */
+};
+
+/*
+ */
+static int alc_build_controls(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int err;
+       int i;
+
+       for (i = 0; i < spec->num_mixers; i++) {
+               err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
+               if (err < 0)
+                       return err;
+       }
+
+       if (spec->multiout.dig_out_nid) {
+               err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+               if (err < 0)
+                       return err;
+       }
+       if (spec->dig_in_nid) {
+               err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+/*
+ * initialize the codec volumes, etc
+ */
+
+static struct hda_verb alc880_init_verbs_three_stack[] = {
+       /* Line In pin widget for input */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+       /* CD pin widget for input */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+       /* Mic1 (rear panel) pin widget for input and vref at 80% */
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+       /* Mic2 (front panel) pin widget for input and vref at 80% */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+       /* unmute amp left and right */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+       /* set connection select to line in (default select for this ADC) */
+       {0x07, AC_VERB_SET_CONNECT_SEL, 0x02},
+       /* unmute front mixer amp left (volume = 0) */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* mute pin widget amp left and right (no gain on this amp) */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+       /* unmute rear mixer amp left and right (volume = 0) */
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* mute pin widget amp left and right (no gain on this amp) */
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+       /* unmute rear mixer amp left and right (volume = 0) */
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* mute pin widget amp left and right (no gain on this amp) */
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+
+       /* using rear surround as the path for headphone output */
+       /* unmute rear surround mixer amp left and right (volume = 0) */
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* PASD 3 stack boards use the Mic 2 as the headphone output */
+       /* need to program the selector associated with the Mic 2 pin widget to
+        * surround path (index 0x01) for headphone output */
+       {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
+       /* mute pin widget amp left and right (no gain on this amp) */
+       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+       /* need to retask the Mic 2 pin widget to output */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+
+       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) for mixer widget(nid=0x0B)
+        * to support the input path of analog loopback
+        * Note: PASD motherboards uses the Line In 2 as the input for front panel
+        * mic (mic 2)
+        */
+       /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
+       /* unmute CD */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+       /* unmute Line In */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+       /* unmute Mic 1 */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       /* unmute Line In 2 (for PASD boards Mic 2) */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+
+       /* Unmute input amps for the line out paths to support the output path of
+        * analog loopback
+        * the mixers on the output path has 2 inputs, one from the DAC and one
+        * from the mixer
+        */
+       /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
+       /* Unmute Front out path */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       /* Unmute Surround (used as HP) out path */
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       /* Unmute C/LFE out path */
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, /* mute */
+       /* Unmute rear Surround out path */
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+
+       { }
+};
+
+static struct hda_verb alc880_init_verbs_five_stack[] = {
+       /* Line In pin widget for input */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+       /* CD pin widget for input */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+       /* Mic1 (rear panel) pin widget for input and vref at 80% */
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+       /* Mic2 (front panel) pin widget for input and vref at 80% */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+       /* unmute amp left and right */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+       /* set connection select to line in (default select for this ADC) */
+       {0x07, AC_VERB_SET_CONNECT_SEL, 0x02},
+       /* unmute front mixer amp left and right (volume = 0) */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* mute pin widget amp left and right (no gain on this amp) */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+       /* five rear and clfe */
+       /* unmute rear mixer amp left and right (volume = 0)  */
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* mute pin widget amp left and right (no gain on this amp) */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+       /* unmute clfe mixer amp left and right (volume = 0) */
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* mute pin widget amp left and right (no gain on this amp) */
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+
+       /* using rear surround as the path for headphone output */
+       /* unmute rear surround mixer amp left and right (volume = 0) */
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* PASD 3 stack boards use the Mic 2 as the headphone output */
+       /* need to program the selector associated with the Mic 2 pin widget to
+        * surround path (index 0x01) for headphone output
+        */
+       {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
+       /* mute pin widget amp left and right (no gain on this amp) */
+       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+       /* need to retask the Mic 2 pin widget to output */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+
+       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) for mixer
+        * widget(nid=0x0B) to support the input path of analog loopback
+        */
+       /* Note: PASD motherboards uses the Line In 2 as the input for front panel mic (mic 2) */
+       /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03*/
+       /* unmute CD */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+       /* unmute Line In */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+       /* unmute Mic 1 */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       /* unmute Line In 2 (for PASD boards Mic 2) */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+
+       /* Unmute input amps for the line out paths to support the output path of
+        * analog loopback
+        * the mixers on the output path has 2 inputs, one from the DAC and
+        * one from the mixer
+        */
+       /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
+       /* Unmute Front out path */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       /* Unmute Surround (used as HP) out path */
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       /* Unmute C/LFE out path */
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, /* mute */
+       /* Unmute rear Surround out path */
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+
+       { }
+};
+
+static struct hda_verb alc880_w810_init_verbs[] = {
+       /* front channel selector/amp: input 0: DAC: unmuted, (no volume selection) */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+
+       /* front channel selector/amp: input 1: capture mix: muted, (no volume selection) */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180},
+
+       /* front channel selector/amp: output 0: unmuted, max volume */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+
+       /* front out pin: muted, (no volume selection)  */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+
+       /* front out pin: NOT headphone enable, out enable, vref disabled */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+
+
+       /* surround channel selector/amp: input 0: DAC: unmuted, (no volume selection) */
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+
+       /* surround channel selector/amp: input 1: capture mix: muted, (no volume selection) */
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180},
+
+       /* surround channel selector/amp: output 0: unmuted, max volume */
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+
+       /* surround out pin: muted, (no volume selection)  */
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+
+       /* surround out pin: NOT headphone enable, out enable, vref disabled */
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+
+
+       /* c/lfe channel selector/amp: input 0: DAC: unmuted, (no volume selection) */
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+
+       /* c/lfe channel selector/amp: input 1: capture mix: muted, (no volume selection) */
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180},
+
+       /* c/lfe channel selector/amp: output 0: unmuted, max volume */
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+
+       /* c/lfe out pin: muted, (no volume selection)  */
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+
+       /* c/lfe out pin: NOT headphone enable, out enable, vref disabled */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+
+
+       /* hphone/speaker input selector: front DAC */
+       {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
+
+       /* hphone/speaker out pin: muted, (no volume selection)  */
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+
+       /* hphone/speaker out pin: NOT headphone enable, out enable, vref disabled */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+
+
+       { }
+};
+
+static int alc_init(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       snd_hda_sequence_write(codec, spec->init_verbs);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * resume
+ */
+static int alc_resume(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int i;
+
+       alc_init(codec);
+       for (i = 0; i < spec->num_mixers; i++) {
+               snd_hda_resume_ctls(codec, spec->mixers[i]);
+       }
+       if (spec->multiout.dig_out_nid)
+               snd_hda_resume_spdif_out(codec);
+       if (spec->dig_in_nid)
+               snd_hda_resume_spdif_in(codec);
+
+       return 0;
+}
+#endif
+
+/*
+ * Analog playback callbacks
+ */
+static int alc880_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                   struct hda_codec *codec,
+                                   snd_pcm_substream_t *substream)
+{
+       struct alc_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int alc880_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      unsigned int stream_tag,
+                                      unsigned int format,
+                                      snd_pcm_substream_t *substream)
+{
+       struct alc_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+                                               format, substream);
+}
+
+static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      snd_pcm_substream_t *substream)
+{
+       struct alc_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int alc880_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       snd_pcm_substream_t *substream)
+{
+       struct alc_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+                                        struct hda_codec *codec,
+                                        snd_pcm_substream_t *substream)
+{
+       struct alc_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     unsigned int stream_tag,
+                                     unsigned int format,
+                                     snd_pcm_substream_t *substream)
+{
+       struct alc_spec *spec = codec->spec;
+
+       snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+                                  stream_tag, 0, format);
+       return 0;
+}
+
+static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     snd_pcm_substream_t *substream)
+{
+       struct alc_spec *spec = codec->spec;
+
+       snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
+       return 0;
+}
+
+
+/*
+ */
+static struct hda_pcm_stream alc880_pcm_analog_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 8,
+       .nid = 0x02, /* NID to query formats and rates */
+       .ops = {
+               .open = alc880_playback_pcm_open,
+               .prepare = alc880_playback_pcm_prepare,
+               .cleanup = alc880_playback_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream alc880_pcm_analog_capture = {
+       .substreams = 2,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = 0x07, /* NID to query formats and rates */
+       .ops = {
+               .prepare = alc880_capture_pcm_prepare,
+               .cleanup = alc880_capture_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream alc880_pcm_digital_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       /* NID is set in alc_build_pcms */
+       .ops = {
+               .open = alc880_dig_playback_pcm_open,
+               .close = alc880_dig_playback_pcm_close
+       },
+};
+
+static struct hda_pcm_stream alc880_pcm_digital_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       /* NID is set in alc_build_pcms */
+};
+
+static int alc_build_pcms(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       struct hda_pcm *info = spec->pcm_rec;
+       int i;
+
+       codec->num_pcms = 1;
+       codec->pcm_info = info;
+
+       info->name = spec->stream_name_analog;
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
+       info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
+
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
+       for (i = 0; i < spec->num_channel_mode; i++) {
+               if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
+                   info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
+               }
+       }
+
+       if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+               codec->num_pcms++;
+               info++;
+               info->name = spec->stream_name_digital;
+               if (spec->multiout.dig_out_nid) {
+                       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback);
+                       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+               }
+               if (spec->dig_in_nid) {
+                       info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture);
+                       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
+               }
+       }
+
+       return 0;
+}
+
+static void alc_free(struct hda_codec *codec)
+{
+       kfree(codec->spec);
+}
+
+/*
+ */
+static struct hda_codec_ops alc_patch_ops = {
+       .build_controls = alc_build_controls,
+       .build_pcms = alc_build_pcms,
+       .init = alc_init,
+       .free = alc_free,
+#ifdef CONFIG_PM
+       .resume = alc_resume,
+#endif
+};
+
+/*
+ */
+
+static struct hda_board_config alc880_cfg_tbl[] = {
+       /* Back 3 jack, front 2 jack */
+       { .modelname = "3stack", .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe200, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe201, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe202, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe203, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe204, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe205, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe206, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe207, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe208, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe209, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe20a, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe20b, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe20c, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe20d, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe20e, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe20f, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe210, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe211, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe214, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe302, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe303, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe304, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe306, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe307, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xe404, .config = ALC880_3ST },
+       { .pci_vendor = 0x8086, .pci_device = 0xa101, .config = ALC880_3ST },
+       { .pci_vendor = 0x107b, .pci_device = 0x3031, .config = ALC880_3ST },
+       { .pci_vendor = 0x107b, .pci_device = 0x4036, .config = ALC880_3ST },
+       { .pci_vendor = 0x107b, .pci_device = 0x4037, .config = ALC880_3ST },
+       { .pci_vendor = 0x107b, .pci_device = 0x4038, .config = ALC880_3ST },
+       { .pci_vendor = 0x107b, .pci_device = 0x4040, .config = ALC880_3ST },
+       { .pci_vendor = 0x107b, .pci_device = 0x4041, .config = ALC880_3ST },
+
+       /* Back 3 jack, front 2 jack (Internal add Aux-In) */
+       { .pci_vendor = 0x1025, .pci_device = 0xe310, .config = ALC880_3ST },
+
+       /* Back 3 jack plus 1 SPDIF out jack, front 2 jack */
+       { .modelname = "3stack-digout", .config = ALC880_3ST_DIG },
+       { .pci_vendor = 0x8086, .pci_device = 0xe308, .config = ALC880_3ST_DIG },
+
+       /* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/
+       { .pci_vendor = 0x8086, .pci_device = 0xe305, .config = ALC880_3ST_DIG },
+       { .pci_vendor = 0x8086, .pci_device = 0xd402, .config = ALC880_3ST_DIG },
+       { .pci_vendor = 0x1025, .pci_device = 0xe309, .config = ALC880_3ST_DIG },
+
+       /* Back 5 jack, front 2 jack */
+       { .modelname = "5stack", .config = ALC880_5ST },
+       { .pci_vendor = 0x107b, .pci_device = 0x3033, .config = ALC880_5ST },
+       { .pci_vendor = 0x107b, .pci_device = 0x4039, .config = ALC880_5ST },
+       { .pci_vendor = 0x107b, .pci_device = 0x3032, .config = ALC880_5ST },
+       { .pci_vendor = 0x103c, .pci_device = 0x2a09, .config = ALC880_5ST },
+
+       /* Back 5 jack plus 1 SPDIF out jack, front 2 jack */
+       { .modelname = "5stack-digout", .config = ALC880_5ST_DIG },
+       { .pci_vendor = 0x8086, .pci_device = 0xe224, .config = ALC880_5ST_DIG },
+       { .pci_vendor = 0x8086, .pci_device = 0xe400, .config = ALC880_5ST_DIG },
+       { .pci_vendor = 0x8086, .pci_device = 0xe401, .config = ALC880_5ST_DIG },
+       { .pci_vendor = 0x8086, .pci_device = 0xe402, .config = ALC880_5ST_DIG },
+       { .pci_vendor = 0x8086, .pci_device = 0xd400, .config = ALC880_5ST_DIG },
+       { .pci_vendor = 0x8086, .pci_device = 0xd401, .config = ALC880_5ST_DIG },
+       { .pci_vendor = 0x8086, .pci_device = 0xa100, .config = ALC880_5ST_DIG },
+       { .pci_vendor = 0x1565, .pci_device = 0x8202, .config = ALC880_5ST_DIG },
+
+       { .modelname = "w810", .config = ALC880_W810 },
+       { .pci_vendor = 0x161f, .pci_device = 0x203d, .config = ALC880_W810 },
+
+       {}
+};
+
+static int patch_alc880(struct hda_codec *codec)
+{
+       struct alc_spec *spec;
+       int board_config;
+
+       spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       board_config = snd_hda_check_board_config(codec, alc880_cfg_tbl);
+       if (board_config < 0) {
+               snd_printd(KERN_INFO "hda_codec: Unknown model for ALC880\n");
+               board_config = ALC880_MINIMAL;
+       }
+
+       switch (board_config) {
+       case ALC880_W810:
+               spec->mixers[spec->num_mixers] = alc880_w810_base_mixer;
+               spec->num_mixers++;
+               break;
+       case ALC880_5ST:
+       case ALC880_5ST_DIG:
+               spec->mixers[spec->num_mixers] = alc880_five_stack_mixer;
+               spec->num_mixers++;
+               break;
+       default:
+               spec->mixers[spec->num_mixers] = alc880_base_mixer;
+               spec->num_mixers++;
+               break;
+       }
+
+       switch (board_config) {
+       case ALC880_3ST_DIG:
+       case ALC880_5ST_DIG:
+       case ALC880_W810:
+               spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
+               break;
+       default:
+               break;
+       }
+
+       switch (board_config) {
+       case ALC880_3ST:
+       case ALC880_3ST_DIG:
+       case ALC880_5ST:
+       case ALC880_5ST_DIG:
+       case ALC880_W810:
+               spec->front_panel = 1;
+               break;
+       default:
+               break;
+       }
+
+       switch (board_config) {
+       case ALC880_5ST:
+       case ALC880_5ST_DIG:
+               spec->init_verbs = alc880_init_verbs_five_stack;
+               spec->channel_mode = alc880_fivestack_modes;
+               spec->num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes);
+               break;
+       case ALC880_W810:
+               spec->init_verbs = alc880_w810_init_verbs;
+               spec->channel_mode = alc880_w810_modes;
+               spec->num_channel_mode = ARRAY_SIZE(alc880_w810_modes);
+               break;
+       default:
+               spec->init_verbs = alc880_init_verbs_three_stack;
+               spec->channel_mode = alc880_threestack_modes;
+               spec->num_channel_mode = ARRAY_SIZE(alc880_threestack_modes);
+               break;
+       }
+
+       spec->stream_name_analog = "ALC880 Analog";
+       spec->stream_analog_playback = &alc880_pcm_analog_playback;
+       spec->stream_analog_capture = &alc880_pcm_analog_capture;
+
+       spec->stream_name_digital = "ALC880 Digital";
+       spec->stream_digital_playback = &alc880_pcm_digital_playback;
+       spec->stream_digital_capture = &alc880_pcm_digital_capture;
+
+       spec->multiout.max_channels = spec->channel_mode[0].channels;
+
+       switch (board_config) {
+       case ALC880_W810:
+               spec->multiout.num_dacs = ARRAY_SIZE(alc880_w810_dac_nids);
+               spec->multiout.dac_nids = alc880_w810_dac_nids;
+               // No dedicated headphone socket - it's shared with built-in speakers.
+               break;
+       default:
+               spec->multiout.num_dacs = ARRAY_SIZE(alc880_dac_nids);
+               spec->multiout.dac_nids = alc880_dac_nids;
+               spec->multiout.hp_nid = 0x03; /* rear-surround NID */
+               break;
+       }
+
+       spec->input_mux = &alc880_capture_source;
+       spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
+       spec->adc_nids = alc880_adc_nids;
+
+       codec->patch_ops = alc_patch_ops;
+
+       return 0;
+}
+
+/*
+ * ALC260 support
+ */
+
+/*
+ * This is just place-holder, so there's something for alc_build_pcms to look
+ * at when it calculates the maximum number of channels. ALC260 has no mixer
+ * element which allows changing the channel mode, so the verb list is
+ * never used.
+ */
+static struct alc_channel_mode alc260_modes[1] = {
+       { 2, NULL },
+};
+
+snd_kcontrol_new_t alc260_base_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
+       /* use LINE2 for the output */
+       /* HDA_CODEC_MUTE("Front Playback Switch", 0x0f, 0x0, HDA_OUTPUT), */
+       HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x07, 0x05, HDA_INPUT),
+       HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x07, 0x05, HDA_INPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Capture Source",
+               .info = alc_mux_enum_info,
+               .get = alc_mux_enum_get,
+               .put = alc_mux_enum_put,
+       },
+       { } /* end */
+};
+
+static struct hda_verb alc260_init_verbs[] = {
+       /* Line In pin widget for input */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+       /* CD pin widget for input */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+       /* Mic1 (rear panel) pin widget for input and vref at 80% */
+       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+       /* Mic2 (front panel) pin widget for input and vref at 80% */
+       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+       /* LINE-2 is used for line-out in rear */
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       /* select line-out */
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* LINE-OUT pin */
+       {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       /* enable HP */
+       {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       /* enable Mono */
+       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       /* unmute amp left and right */
+       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+       /* set connection select to line in (default select for this ADC) */
+       {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
+       /* unmute Line-Out mixer amp left and right (volume = 0) */
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* mute pin widget amp left and right (no gain on this amp) */
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* unmute HP mixer amp left and right (volume = 0) */
+       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* mute pin widget amp left and right (no gain on this amp) */
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* unmute Mono mixer amp left and right (volume = 0) */
+       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* mute pin widget amp left and right (no gain on this amp) */
+       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* mute LINE-2 out */
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
+       /* unmute CD */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+       /* unmute Line In */
+       {0x07,  AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+       /* unmute Mic */
+       {0x07,  AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
+       /* Unmute Front out path */
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       /* Unmute Headphone out path */
+       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       /* Unmute Mono out path */
+       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       { }
+};
+
+static struct hda_pcm_stream alc260_pcm_analog_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = 0x2,
+};
+
+static struct hda_pcm_stream alc260_pcm_analog_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = 0x4,
+};
+
+static int patch_alc260(struct hda_codec *codec)
+{
+       struct alc_spec *spec;
+
+       spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       spec->mixers[spec->num_mixers] = alc260_base_mixer;
+       spec->num_mixers++;
+
+       spec->init_verbs = alc260_init_verbs;
+       spec->channel_mode = alc260_modes;
+       spec->num_channel_mode = ARRAY_SIZE(alc260_modes);
+
+       spec->stream_name_analog = "ALC260 Analog";
+       spec->stream_analog_playback = &alc260_pcm_analog_playback;
+       spec->stream_analog_capture = &alc260_pcm_analog_capture;
+
+       spec->multiout.max_channels = spec->channel_mode[0].channels;
+       spec->multiout.num_dacs = ARRAY_SIZE(alc260_dac_nids);
+       spec->multiout.dac_nids = alc260_dac_nids;
+
+       spec->input_mux = &alc260_capture_source;
+       spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
+       spec->adc_nids = alc260_adc_nids;
+
+       codec->patch_ops = alc_patch_ops;
+
+       return 0;
+}
+
+/*
+ * ALC882 support
+ *
+ * ALC882 is almost identical with ALC880 but has cleaner and more flexible
+ * configuration.  Each pin widget can choose any input DACs and a mixer.
+ * Each ADC is connected from a mixer of all inputs.  This makes possible
+ * 6-channel independent captures.
+ *
+ * In addition, an independent DAC for the multi-playback (not used in this
+ * driver yet).
+ */
+
+static struct alc_channel_mode alc882_ch_modes[1] = {
+       { 8, NULL }
+};
+
+static hda_nid_t alc882_dac_nids[4] = {
+       /* front, rear, clfe, rear_surr */
+       0x02, 0x03, 0x04, 0x05
+};
+
+static hda_nid_t alc882_adc_nids[3] = {
+       /* ADC0-2 */
+       0x07, 0x08, 0x09,
+};
+
+/* input MUX */
+/* FIXME: should be a matrix-type input source selection */
+
+static struct hda_input_mux alc882_capture_source = {
+       .num_items = 4,
+       .items = {
+               { "Mic", 0x0 },
+               { "Front Mic", 0x1 },
+               { "Line", 0x2 },
+               { "CD", 0x4 },
+       },
+};
+
+#define alc882_mux_enum_info alc_mux_enum_info
+#define alc882_mux_enum_get alc_mux_enum_get
+
+static int alc882_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       const struct hda_input_mux *imux = spec->input_mux;
+       unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+       static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 };
+       hda_nid_t nid = capture_mixers[adc_idx];
+       unsigned int *cur_val = &spec->cur_mux[adc_idx];
+       unsigned int i, idx;
+
+       idx = ucontrol->value.enumerated.item[0];
+       if (idx >= imux->num_items)
+               idx = imux->num_items - 1;
+       if (*cur_val == idx && ! codec->in_resume)
+               return 0;
+       for (i = 0; i < imux->num_items; i++) {
+               unsigned int v = (i == idx) ? 0x7000 : 0x7080;
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                                   v | (imux->items[i].index << 8));
+       }
+       *cur_val = idx;
+       return 1;
+}
+
+/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
+ *                 Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
+ */
+static snd_kcontrol_new_t alc882_base_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Side Playback Switch", 0x17, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+       HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               /* .name = "Capture Source", */
+               .name = "Input Source",
+               .count = 3,
+               .info = alc882_mux_enum_info,
+               .get = alc882_mux_enum_get,
+               .put = alc882_mux_enum_put,
+       },
+       { } /* end */
+};
+
+static struct hda_verb alc882_init_verbs[] = {
+       /* Front mixer: unmute input/output amp left and right (volume = 0) */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       /* Rear mixer */
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       /* CLFE mixer */
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       /* Side mixer */
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+
+       /* Front Pin: to output mode */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       /* Front Pin: mute amp left and right (no volume) */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+       /* select Front mixer (0x0c, index 0) */
+       {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* Rear Pin */
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       /* Rear Pin: mute amp left and right (no volume) */
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+       /* select Rear mixer (0x0d, index 1) */
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+       /* CLFE Pin */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       /* CLFE Pin: mute amp left and right (no volume) */
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+       /* select CLFE mixer (0x0e, index 2) */
+       {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
+       /* Side Pin */
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       /* Side Pin: mute amp left and right (no volume) */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+       /* select Side mixer (0x0f, index 3) */
+       {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+       /* Headphone Pin */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       /* Headphone Pin: mute amp left and right (no volume) */
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
+       /* select Front mixer (0x0c, index 0) */
+       {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* Mic (rear) pin widget for input and vref at 80% */
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+       /* Front Mic pin widget for input and vref at 80% */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+       /* Line In pin widget for input */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+       /* CD pin widget for input */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+
+       /* FIXME: use matrix-type input source selection */
+       /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+       /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+       /* Input mixer2 */
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+       /* Input mixer3 */
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+       /* ADC1: unmute amp left and right */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+       /* ADC2: unmute amp left and right */
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+       /* ADC3: unmute amp left and right */
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+
+       /* Unmute front loopback */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       /* Unmute rear loopback */
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       /* Mute CLFE loopback */
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
+       /* Unmute side loopback */
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+
+       { }
+};
+
+static int patch_alc882(struct hda_codec *codec)
+{
+       struct alc_spec *spec;
+
+       spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       spec->mixers[spec->num_mixers] = alc882_base_mixer;
+       spec->num_mixers++;
+
+       spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
+       spec->dig_in_nid = ALC880_DIGIN_NID;
+       spec->front_panel = 1;
+       spec->init_verbs = alc882_init_verbs;
+       spec->channel_mode = alc882_ch_modes;
+       spec->num_channel_mode = ARRAY_SIZE(alc882_ch_modes);
+
+       spec->stream_name_analog = "ALC882 Analog";
+       spec->stream_analog_playback = &alc880_pcm_analog_playback;
+       spec->stream_analog_capture = &alc880_pcm_analog_capture;
+
+       spec->stream_name_digital = "ALC882 Digital";
+       spec->stream_digital_playback = &alc880_pcm_digital_playback;
+       spec->stream_digital_capture = &alc880_pcm_digital_capture;
+
+       spec->multiout.max_channels = spec->channel_mode[0].channels;
+       spec->multiout.num_dacs = ARRAY_SIZE(alc882_dac_nids);
+       spec->multiout.dac_nids = alc882_dac_nids;
+
+       spec->input_mux = &alc882_capture_source;
+       spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
+       spec->adc_nids = alc882_adc_nids;
+
+       codec->patch_ops = alc_patch_ops;
+
+       return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_realtek[] = {
+       { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
+       { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
+       { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
+       {} /* terminator */
+};
diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c
new file mode 100644 (file)
index 0000000..d1f9083
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ *   ALSA driver for ICEnsemble ICE1724 (Envy24)
+ *
+ *   Lowlevel functions for Terratec PHASE 22
+ *
+ *     Copyright (c) 2005 Misha Zhilin <misha@epiphan.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
+ *
+ */
+
+/* PHASE 22 overview:
+ *   Audio controller: VIA Envy24HT-S (slightly trimmed down version of Envy24HT)
+ *   Analog chip: AK4524 (partially via Philip's 74HCT125)
+ *   Digital receiver: CS8414-CS (not supported in this release)
+ *
+ *   Envy connects to AK4524
+ *     - CS directly from GPIO 10
+ *     - CCLK via 74HCT125's gate #4 from GPIO 4
+ *     - CDTI via 74HCT125's gate #2 from GPIO 5
+ *             CDTI may be completely blocked by 74HCT125's gate #1 controlled by GPIO 3
+ */
+
+#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 "phase.h"
+
+static akm4xxx_t akm_phase22 __devinitdata = {
+       .type = SND_AK4524,
+       .num_dacs = 2,
+       .num_adcs = 2,
+};
+
+static struct snd_ak4xxx_private akm_phase22_priv __devinitdata = {
+       .caddr =        2,
+       .cif =          1,
+       .data_mask =    1 << 4,
+       .clk_mask =     1 << 5,
+       .cs_mask =      1 << 10,
+       .cs_addr =      1 << 10,
+       .cs_none =      0,
+       .add_flags =    1 << 3,
+       .mask_flags =   0,
+};
+
+static int __devinit phase22_init(ice1712_t *ice)
+{
+       akm4xxx_t *ak;
+       int err;
+
+       // Configure DAC/ADC description for generic part of ice1724
+       switch (ice->eeprom.subvendor) {
+       case VT1724_SUBDEVICE_PHASE22:
+               ice->num_total_dacs = 2;
+               ice->num_total_adcs = 2;
+               ice->vt1720 = 1; // Envy24HT-S have 16 bit wide GPIO
+               break;
+       default:
+               snd_BUG();
+               return -EINVAL;
+       }
+
+       // Initialize analog chips
+       ak = ice->akm = kcalloc(1, sizeof(akm4xxx_t), GFP_KERNEL);
+       if (! ak)
+               return -ENOMEM;
+       ice->akm_codecs = 1;
+       switch (ice->eeprom.subvendor) {
+       case VT1724_SUBDEVICE_PHASE22:
+               if ((err = snd_ice1712_akm4xxx_init(ak, &akm_phase22, &akm_phase22_priv, ice)) < 0)
+                       return err;
+               break;
+       }
+
+       return 0;
+}
+
+static int __devinit phase22_add_controls(ice1712_t *ice)
+{
+       int err = 0;
+
+       switch (ice->eeprom.subvendor) {
+       case VT1724_SUBDEVICE_PHASE22:
+               err = snd_ice1712_akm4xxx_build_controls(ice);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+static unsigned char phase22_eeprom[] __devinitdata = {
+       0x00,   /* SYSCONF: 1xADC, 1xDACs */
+       0x80,   /* ACLINK: I2S */
+       0xf8,   /* I2S: vol, 96k, 24bit*/
+       0xc3,   /* SPDIF: out-en, out-int, spdif-in */
+       0xFF,   /* GPIO_DIR */
+       0xFF,   /* GPIO_DIR1 */
+       0xFF,   /* GPIO_DIR2 */
+       0x00,   /* GPIO_MASK */
+       0x00,   /* GPIO_MASK1 */
+       0x00,   /* GPIO_MASK2 */
+       0x00,   /* GPIO_STATE: */
+       0x00,   /* GPIO_STATE1: */
+       0x00,   /* GPIO_STATE2 */
+};
+
+struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {
+       {
+               .subvendor = VT1724_SUBDEVICE_PHASE22,
+               .name = "Terratec PHASE 22",
+               .model = "phase22",
+               .chip_init = phase22_init,
+               .build_controls = phase22_add_controls,
+               .eeprom_size = sizeof(phase22_eeprom),
+               .eeprom_data = phase22_eeprom,
+       },
+       { } /* terminator */
+};
diff --git a/sound/ppc/toonie.c b/sound/ppc/toonie.c
new file mode 100644 (file)
index 0000000..082bc4b
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Mac Mini "toonie" mixer control
+ *
+ * Copyright (c) 2005 by 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.
+ *
+ *   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/delay.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <sound/core.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include "pmac.h"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt...) printk(fmt)
+#else
+#define DBG(fmt...)
+#endif
+
+struct pmac_gpio {
+       unsigned int addr;
+       u8 active_val;
+       u8 inactive_val;
+       u8 active_state;
+};
+
+struct pmac_toonie
+{
+       struct pmac_gpio        hp_detect_gpio;
+       struct pmac_gpio        hp_mute_gpio;
+       struct pmac_gpio        amp_mute_gpio;
+       int                     hp_detect_irq;
+       int                     auto_mute_notify;
+       struct work_struct      detect_work;
+};
+
+
+/*
+ * gpio access
+ */
+#define do_gpio_write(gp, val) \
+       pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, (gp)->addr, val)
+#define do_gpio_read(gp) \
+       pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, (gp)->addr, 0)
+#define tumbler_gpio_free(gp) /* NOP */
+
+static void write_audio_gpio(struct pmac_gpio *gp, int active)
+{
+       if (! gp->addr)
+               return;
+       active = active ? gp->active_val : gp->inactive_val;
+       do_gpio_write(gp, active);
+       DBG("(I) gpio %x write %d\n", gp->addr, active);
+}
+
+static int check_audio_gpio(struct pmac_gpio *gp)
+{
+       int ret;
+
+       if (! gp->addr)
+               return 0;
+
+       ret = do_gpio_read(gp);
+
+       return (ret & 0xd) == (gp->active_val & 0xd);
+}
+
+static int read_audio_gpio(struct pmac_gpio *gp)
+{
+       int ret;
+       if (! gp->addr)
+               return 0;
+       ret = ((do_gpio_read(gp) & 0x02) !=0);
+       return ret == gp->active_state;
+}
+
+
+enum { TOONIE_MUTE_HP, TOONIE_MUTE_AMP };
+
+static int toonie_get_mute_switch(snd_kcontrol_t *kcontrol,
+                                 snd_ctl_elem_value_t *ucontrol)
+{
+       pmac_t *chip = snd_kcontrol_chip(kcontrol);
+       struct pmac_toonie *mix = chip->mixer_data;
+       struct pmac_gpio *gp;
+
+       if (mix == NULL)
+               return -ENODEV;
+       switch(kcontrol->private_value) {
+       case TOONIE_MUTE_HP:
+               gp = &mix->hp_mute_gpio;
+               break;
+       case TOONIE_MUTE_AMP:
+               gp = &mix->amp_mute_gpio;
+               break;
+       default:
+               return -EINVAL;;
+       }
+       ucontrol->value.integer.value[0] = !check_audio_gpio(gp);
+       return 0;
+}
+
+static int toonie_put_mute_switch(snd_kcontrol_t *kcontrol,
+                                  snd_ctl_elem_value_t *ucontrol)
+{
+       pmac_t *chip = snd_kcontrol_chip(kcontrol);
+       struct pmac_toonie *mix = chip->mixer_data;
+       struct pmac_gpio *gp;
+       int val;
+
+       if (chip->update_automute && chip->auto_mute)
+               return 0; /* don't touch in the auto-mute mode */
+
+       if (mix == NULL)
+               return -ENODEV;
+
+       switch(kcontrol->private_value) {
+       case TOONIE_MUTE_HP:
+               gp = &mix->hp_mute_gpio;
+               break;
+       case TOONIE_MUTE_AMP:
+               gp = &mix->amp_mute_gpio;
+               break;
+       default:
+               return -EINVAL;;
+       }
+       val = ! check_audio_gpio(gp);
+       if (val != ucontrol->value.integer.value[0]) {
+               write_audio_gpio(gp, ! ucontrol->value.integer.value[0]);
+               return 1;
+       }
+       return 0;
+}
+
+static snd_kcontrol_new_t toonie_hp_sw __initdata = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Headphone Playback Switch",
+       .info = snd_pmac_boolean_mono_info,
+       .get = toonie_get_mute_switch,
+       .put = toonie_put_mute_switch,
+       .private_value = TOONIE_MUTE_HP,
+};
+static snd_kcontrol_new_t toonie_speaker_sw __initdata = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "PC Speaker Playback Switch",
+       .info = snd_pmac_boolean_mono_info,
+       .get = toonie_get_mute_switch,
+       .put = toonie_put_mute_switch,
+       .private_value = TOONIE_MUTE_AMP,
+};
+
+/*
+ * auto-mute stuffs
+ */
+static int toonie_detect_headphone(pmac_t *chip)
+{
+       struct pmac_toonie *mix = chip->mixer_data;
+       int detect = 0;
+
+       if (mix->hp_detect_gpio.addr)
+               detect |= read_audio_gpio(&mix->hp_detect_gpio);
+       return detect;
+}
+
+static void toonie_check_mute(pmac_t *chip, struct pmac_gpio *gp, int val,
+                             int do_notify, snd_kcontrol_t *sw)
+{
+       if (check_audio_gpio(gp) != val) {
+               write_audio_gpio(gp, val);
+               if (do_notify)
+                       snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                                      &sw->id);
+       }
+}
+
+static void toonie_detect_handler(void *self)
+{
+       pmac_t *chip = (pmac_t*) self;
+       struct pmac_toonie *mix;
+       int headphone;
+
+       if (!chip)
+               return;
+
+       mix = chip->mixer_data;
+       snd_assert(mix, return);
+
+       headphone = toonie_detect_headphone(chip);
+
+       DBG("headphone: %d, lineout: %d\n", headphone, lineout);
+
+       if (headphone) {
+               /* unmute headphone/lineout & mute speaker */
+               toonie_check_mute(chip, &mix->hp_mute_gpio, 0,
+                                 mix->auto_mute_notify, chip->master_sw_ctl);
+               toonie_check_mute(chip, &mix->amp_mute_gpio, 1,
+                                 mix->auto_mute_notify, chip->speaker_sw_ctl);
+       } else {
+               /* unmute speaker, mute others */
+               toonie_check_mute(chip, &mix->amp_mute_gpio, 0,
+                                 mix->auto_mute_notify, chip->speaker_sw_ctl);
+               toonie_check_mute(chip, &mix->hp_mute_gpio, 1,
+                                 mix->auto_mute_notify, chip->master_sw_ctl);
+       }
+       if (mix->auto_mute_notify) {
+               snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                                      &chip->hp_detect_ctl->id);
+       }
+}
+
+static void toonie_update_automute(pmac_t *chip, int do_notify)
+{
+       if (chip->auto_mute) {
+               struct pmac_toonie *mix;
+               mix = chip->mixer_data;
+               snd_assert(mix, return);
+               mix->auto_mute_notify = do_notify;
+               schedule_work(&mix->detect_work);
+       }
+}
+
+/* interrupt - headphone plug changed */
+static irqreturn_t toonie_hp_intr(int irq, void *devid, struct pt_regs *regs)
+{
+       pmac_t *chip = devid;
+
+       if (chip->update_automute && chip->initialized) {
+               chip->update_automute(chip, 1);
+               return IRQ_HANDLED;
+       }
+       return IRQ_NONE;
+}
+
+/* look for audio gpio device */
+static int find_audio_gpio(const char *name, const char *platform,
+                          struct pmac_gpio *gp)
+{
+       struct device_node *np;
+       u32 *base, addr;
+
+       if (! (np = find_devices("gpio")))
+               return -ENODEV;
+
+       for (np = np->child; np; np = np->sibling) {
+               char *property = get_property(np, "audio-gpio", NULL);
+               if (property && strcmp(property, name) == 0)
+                       break;
+               if (device_is_compatible(np, name))
+                       break;
+       }
+       if (np == NULL)
+               return -ENODEV;
+
+       base = (u32 *)get_property(np, "AAPL,address", NULL);
+       if (! base) {
+               base = (u32 *)get_property(np, "reg", NULL);
+               if (!base) {
+                       DBG("(E) cannot find address for device %s !\n", name);
+                       return -ENODEV;
+               }
+               addr = *base;
+               if (addr < 0x50)
+                       addr += 0x50;
+       } else
+               addr = *base;
+
+       gp->addr = addr & 0x0000ffff;
+
+       /* Try to find the active state, default to 0 ! */
+       base = (u32 *)get_property(np, "audio-gpio-active-state", NULL);
+       if (base) {
+               gp->active_state = *base;
+               gp->active_val = (*base) ? 0x5 : 0x4;
+               gp->inactive_val = (*base) ? 0x4 : 0x5;
+       } else {
+               u32 *prop = NULL;
+               gp->active_state = 0;
+               gp->active_val = 0x4;
+               gp->inactive_val = 0x5;
+               /* Here are some crude hacks to extract the GPIO polarity and
+                * open collector informations out of the do-platform script
+                * as we don't yet have an interpreter for these things
+                */
+               if (platform)
+                       prop = (u32 *)get_property(np, platform, NULL);
+               if (prop) {
+                       if (prop[3] == 0x9 && prop[4] == 0x9) {
+                               gp->active_val = 0xd;
+                               gp->inactive_val = 0xc;
+                       }
+                       if (prop[3] == 0x1 && prop[4] == 0x1) {
+                               gp->active_val = 0x5;
+                               gp->inactive_val = 0x4;
+                       }
+               }
+       }
+
+       DBG("(I) GPIO device %s found, offset: %x, active state: %d !\n",
+           name, gp->addr, gp->active_state);
+
+       return (np->n_intrs > 0) ? np->intrs[0].line : 0;
+}
+
+static void toonie_cleanup(pmac_t *chip)
+{
+       struct pmac_toonie *mix = chip->mixer_data;
+       if (! mix)
+               return;
+       if (mix->hp_detect_irq >= 0)
+               free_irq(mix->hp_detect_irq, chip);
+       kfree(mix);
+       chip->mixer_data = NULL;
+}
+
+int snd_pmac_toonie_init(pmac_t *chip)
+{
+       struct pmac_toonie *mix;
+
+       mix = kmalloc(sizeof(*mix), GFP_KERNEL);
+       if (! mix)
+               return -ENOMEM;
+
+       chip->mixer_data = mix;
+       chip->mixer_free = toonie_cleanup;
+
+       find_audio_gpio("headphone-mute", NULL, &mix->hp_mute_gpio);
+       find_audio_gpio("amp-mute", NULL, &mix->amp_mute_gpio);
+       mix->hp_detect_irq = find_audio_gpio("headphone-detect",
+                                            NULL, &mix->hp_detect_gpio);
+
+       strcpy(chip->card->mixername, "PowerMac Toonie");
+
+       chip->master_sw_ctl = snd_ctl_new1(&toonie_hp_sw, chip);
+       snd_ctl_add(chip->card, chip->master_sw_ctl);
+
+       chip->speaker_sw_ctl = snd_ctl_new1(&toonie_speaker_sw, chip);
+       snd_ctl_add(chip->card, chip->speaker_sw_ctl);
+
+       INIT_WORK(&mix->detect_work, toonie_detect_handler, (void *)chip);
+
+       if (mix->hp_detect_irq >= 0) {
+               snd_pmac_add_automute(chip);
+
+               chip->detect_headphone = toonie_detect_headphone;
+               chip->update_automute = toonie_update_automute;
+               toonie_update_automute(chip, 0);
+
+               if (request_irq(mix->hp_detect_irq, toonie_hp_intr, 0,
+                               "Sound Headphone Detection", chip) < 0)
+                       mix->hp_detect_irq = -1;
+       }
+
+       return 0;
+}
+